web-dev-qa-db-fra.com

fonction virtuelle pure avec implémentation

Ma compréhension de base est qu'il n'y a pas d'implémentation pour une fonction virtuelle pure, cependant, on m'a dit qu'il pourrait y avoir une implémentation pour une fonction virtuelle pure.

class A {
public:
    virtual void f() = 0;
};

void A::f() {
    cout<<"Test"<<endl;
}

Le code ci-dessus est-il OK?

A quoi sert-il d'en faire une fonction virtuelle pure avec une implémentation?

159
skydoor

Une fonction pure virtual doit être implémentée dans un type dérivé qui sera instancié directement. Toutefois, le type de base peut toujours définir une implémentation. Une classe dérivée peut appeler explicitement l'implémentation de la classe de base (si les autorisations d'accès le permettent) en utilisant un nom complet (en appelant A::f() dans votre exemple - si A::f() était public ou protected). Quelque chose comme:

class B : public A {

    virtual void f() {
        // class B doesn't have anything special to do for f()
        //  so we'll call A's

        // note that A's declaration of f() would have to be public 
        //  or protected to avoid a compile time problem

        A::f();
    }

};

Le cas d'utilisation auquel je peux penser immédiatement est lorsqu'il existe un comportement par défaut plus ou moins raisonnable, mais le concepteur de la classe souhaite que ce comportement de type par défaut ne soit invoqué que de manière explicite. Vous pouvez également demander aux classes dérivées de toujours effectuer leur propre travail, mais également d’appeler un ensemble commun de fonctionnalités.

Notez que même si le langage le permet, ce n'est pas quelque chose que je vois couramment utilisé (et le fait que cela puisse être fait semble surprendre la plupart des programmeurs C++, même les plus expérimentés).

194
Michael Burr

Pour être clair, vous ne comprenez pas quoi = 0; après une fonction virtuelle signifie.

= 0 signifie que les classes dérivées doivent fournir une implémentation, mais pas que la classe de base ne peut pas fournir une implémentation.

En pratique, lorsque vous marquez une fonction virtuelle comme pure (= 0), il est inutile de fournir une définition, car elle ne sera jamais appelée à moins que quelqu'un le fasse explicitement via Base :: Function (...) ou si Le constructeur de la classe de base appelle la fonction virtuelle en question.

70
Terry Mahaffey

L'avantage de cette méthode est qu'elle oblige les types dérivés à toujours remplacer la méthode, mais fournit également une implémentation par défaut ou additive.

20
JaredPar

Si vous avez du code qui doit être exécuté par la classe dérivée, mais vous ne voulez pas qu'il soit exécuté directement - et vous voulez le forcer à être remplacé.

Votre code est correct, bien que dans l’ensemble, il ne s’agisse pas d’une fonctionnalité souvent utilisée, et n’est généralement visible que lorsqu’on tente de définir un destructeur virtuel pur. Dans ce cas, vous devez fournit une implémentation. Ce qui est amusant, c’est qu’une fois que vous dérivez de cette classe, vous n’avez pas besoin de remplacer le destructeur.

Par conséquent, l’utilisation raisonnable des fonctions virtuelles pures consiste à spécifier un destructeur virtuel pur en tant que mot clé "non final".

Le code suivant est étonnamment correct:

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

Base::~Base() {}

class Derived : public Base {};

int main() { 
  // Base b; -- compile error
  Derived d; 
}
17
Kornel Kisielewicz

Vous devez par exemple donner un corps à un destructeur virtuel pur :)

Lire: http://cplusplus.co.il/2009/08/22/pure-virtual-destructor/

(Lien cassé, utiliser une archive)

5
rmn

Les fonctions virtuelles pures avec ou sans corps signifient simplement que les types dérivés doivent fournir leur propre implémentation.

Les corps de fonction virtuels purs de la classe de base sont utiles si vos classes dérivées veulent appeler l'implémentation de la classe de base.

4
Brian R. Bondy

Oui c'est correct. Dans votre exemple, les classes dérivées de A héritent à la fois de l'interface f()) et d'une implémentation par défaut. Mais vous forcez les classes dérivées à implémenter la méthode f() = (même s'il ne s'agit que d'appeler l'implémentation par défaut fournie par A).

Scott Meyers en parle dans Effective C++ (2nd Edition) Article n ° 36 Différencier l’héritage de l’interface et l’héritage de l’implémentation. Le numéro d'article peut avoir changé dans la dernière édition.

4
Yukiko

Le 'void virtuel foo () = 0;' la syntaxe ne signifie pas que vous ne pouvez pas implémenter foo () dans la classe actuelle, vous le pouvez. Cela ne signifie pas non plus que vous devez l'implémenter dans des classes dérivées. Avant de me gifler, observons le problème de Diamond: (code implicite, remarquez).

class A
{
public: 
    virtual void foo()=0;
    virtual void bar();
}

class B : public virtual A
{
public:
    void foo() { bar(); }
}

class C : public virtual A
{
public:
    void bar();
}

class D : public B, public C
{}

int main(int argc, const char* argv[])
{
    A* obj = new D();
    **obj->foo();**
    return 0;
}

Maintenant, l'invocation obj-> foo () donnera B :: foo () puis C :: bar ().

Vous voyez ... les méthodes virtuelles pures ne doivent pas nécessairement être implémentées dans les classes dérivées (foo () n'a pas d'implémentation dans la classe C - le compilateur compilera) En C++, il y a beaucoup de lacunes.

J'espère pouvoir aider :-)

2
Nir Hedvat