Est-ce que final implique le dépassement?

Si je comprends bien, le mot clé override indique qu'une déclaration donnée implémente une méthode de base virtual, et la compilation devrait échouer s'il n'y a pas de méthode de base correspondante trouvée.

Ma compréhension du mot-clé final est qu'il indique au compilateur qu'aucune classe ne doit remplacer cette fonction virtual.

Est Donc override final redondant? Il semble bien compiler . Quelles informations override final véhiculent que final ne transmet pas? Quel est le cas d'utilisation d'une telle combinaison?

38
demandé sur Columbo 2015-04-02 14:58:45

5 réponses

final ne nécessite pas que la fonction remplace quoi que ce soit en premier lieu. Son effet est défini dans [classe.virtuel]/4

Si une fonction virtuelle f dans une classe B est marquée avec virt-rédacteur de devis final et dans une classe D dérivée à partir de B une fonction D::f remplace B::f, le programme est mal formé.

C'est ça. Maintenant {[10] } signifierait simplement
"Cette fonction remplace une classe de base one (override) et ne peut pas être remplacée lui-même (final)."
final en soi, cela imposerait une exigence plus faible. override et {[2] } ont un comportement indépendant.


Notez que final ne peut être utilisé que pour les fonctions virtuelles - [class.mem] / 8

Un virt-spécificateur-seq doit apparaître que dans la déclaration d'un fonction de membre virtuel (10.3).

D'où la déclaration

void foo() final;

Est effectivement le même que

virtual void foo() final override;

, Puisque les deux nécessitent foo pour remplacer quelque chose - la deuxième déclaration en utilisant override, et la première en étant valide si et seulement si foo est implicitement virtuelle, c'est-à-dire quand foo remplace une fonction virtuelle appelée foo dans une classe de base, ce qui rend foo automatiquement virtuel. ainsi {[11] } serait superflu dans les déclarations où final, mais pas virtual, se produit.
Pourtant, cette dernière déclaration exprime l'intention beaucoup plus clairement et devrait certainement être préférée.

34
répondu Columbo 2015-04-03 12:22:50

final n'implique pas nécessairement que la fonction est remplacée. Il est parfaitement valide (s'il a une valeur quelque peu douteuse) de déclarer une fonction virtuelle comme final sur sa première déclaration dans la hiérarchie d'héritage.

Une raison pour laquelle je peux penser à créer une fonction virtuelle et immédiatement finale est si vous voulezempêcher une classe dérivée de donner le même nom et les paramètres une signification différente.

13
répondu Angew 2015-04-02 12:04:10

(Passez à la fin pour voir la conclusion si vous êtes pressé.)

Les Deux override et final ne peuvent apparaître que dans une déclaration dans une fonction virtuelle. Et les deux mots clés peuvent être utilisés dans la même déclaration de fonction, mais s'il est utile de les utiliser tous les deux dépend des situations.

Prenez le code suivant comme exemple:

#include <iostream>
using std::cout; using std::endl;

struct B {
  virtual void f1() { cout << "B::f1() "; }
  virtual void f2() { cout << "B::f2() "; }
  virtual void f3() { cout << "B::f3() "; }
  virtual void f6() final { cout << "B::f6() "; }
  void f7() { cout << "B::f7() "; }
  void f8() { cout << "B::f8() "; }
  void f9() { cout << "B::f9() "; }
};

struct D : B {
  void f1() override { cout << "D::f1() "; }
  void f2() final { cout << "D::f2() "; }
  void f3() override final { cout << "D::f3() "; }  // need not have override
  // should have override, otherwise add new virtual function
  virtual void f4() final { cout << "D::f4() "; }
  //virtual void f5() override final;  // Error, no virtual function in base class
  //void f6(); // Error, override a final virtual function
  void f7() { cout << "D::f7() "; }
  virtual void f8() { cout << "D::f8() "; }
  //void f9() override;  // Error, override a nonvirtual function 
};

int main() {
  B b; D d;
  B *bp = &b, *bd = &d; D *dp = &d;
  bp->f1(); bp->f2(); bp->f3(); bp->f6(); bp->f7(); bp->f8(); bp->f9(); cout << endl;
  bd->f1(); bd->f2(); bd->f3(); bd->f6(); bd->f7(); bd->f8(); bd->f9(); cout << endl;
  dp->f1(); dp->f2(); dp->f3(); dp->f6(); dp->f7(); dp->f8(); dp->f9(); cout << endl;
  return 0;
}

La sortie est

B::f1() B::f2() B::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() D::f7() D::f8() B::f9()
  1. Comparez f1() et f6(). Nous savons que override et {[3] } sont indépendants sematically.

    • override signifie que la fonction remplace une fonction virtuelle dans sa classe de base. Voir f1() et f3().
    • final signifie que la fonction ne peut pas être remplacée par sa classe dérivée. (Mais la fonction elle-même n'a pas besoin de remplacer une fonction virtuelle de classe de base.) Voir f6() et f4().
  2. Comparez f2() et f3(). Nous savons que si une fonction membre est déclarée sans virtual et avec final, cela signifie qu'elle remplace déjà une fonction virtuelle dans la classe de base. Dans ce cas, le mot clé override est redondant.

  3. Comparez f4() et f5(). Nous savons que si une fonction membre est déclarée avec virtual et si ce n'est pas la première fonction virtuelle dans la hiérarchie d'héritage, alors nous devrions utiliser override pour spécifier la relation de remplacement. Sinon, nous pouvons accidentellement ajouter une nouvelle fonction virtuelle dans la classe dérivée.

  4. Comparez f1() et f7(). Nous savons que toute fonction de membre, pas seulement ceux virtuels, peut être remplacé dans la classe dérivée. Ce que virtual spécifie est polymorphisme, ce qui signifie que la décision quant à la fonction à exécuter est retardée jusqu'à l'exécution et non de la compilation. (Cela devrait être évité dans la pratique.)

  5. Comparez f7() et f8(). Nous savons que nous pouvons même remplacer une fonction de classe de base et en faire une nouvelle fonction virtuelle. (Ce qui signifie que toute fonction membre f8() de classe dérivée de {[29] } sera virtuelle.) (Cela devrait être évité dans la pratique trop.)

  6. Comparez f7() et f9(). Nous savons que override Peut nous aider à trouver l'erreur lorsque nous voulons remplacer une fonction virtuelle dans la classe dérivée tout en oubliant d'ajouter le mot clé virtual dans la classe de base.

En conclusion , la meilleure pratique à mon avis est la suivante:

  • seulement utiliser virtual dans la déclaration de la premier fonction virtuelle dans la classe de base;
  • utilisez toujours override pour spécifier la fonction virtuelle classe dérivée, sauf si final est également spécifié.
4
répondu Jaege 2016-02-25 12:04:34

Non final n'implique pas nécessairement override. En fait, vous pouvez déclarer une fonction virtual que vous déclarez immédiatement final voir ici . Le mot clé final indique simplement qu'aucun class dérivé ne peut créer une substitution de cette fonction.

Le mot clé override est important en ce sens qu'il impose que vous surchargiez effectivement une fonction virtuelle (au lieu de déclarer une nouvelle fonction non liée). Voir ce post concernant l'override

Si longue histoire courte, ils ont chacun servir leur propre usage particulier, et il est souvent correct d'utiliser les deux.

1
répondu CoryKramer 2017-05-23 12:18:10

Le code suivant (avec le spécificateur final) est compilé. Mais la compilation échoue lorsque final est remplacé par override final. Ainsi, override final transmet plus d'informations (et empêche la compilation) que simplement final.

class Base
{
public:
    virtual ~Base() {}
};

class Derived : public Base
{
public:
    virtual void foo() final
    {
        std::cout << "in Derived foo\n";
    }
};

, Essentiellement, override final dit que cette méthode ne peut pas être remplacée dans toute classe dérivée et cette méthode remplace une méthode virtuelle dans une classe de base. final seul ne spécifie pas la partie de remplacement de la classe de base.

1
répondu tcb 2015-09-06 08:28:29