2010-01-23 3 views
11

Lorsque je lis Effective C++, il est dit de ne jamais redéfinir une fonction non-virtuelle en C++.redéfinir une fonction non-virtuelle en C++

Cependant, quand je l'ai testé, le code ci-dessous se compile correctement. Alors, quel est le point? C'est une erreur ou juste une mauvaise pratique?

class A { 

    public: 
    void f() { cout<<"a.f()"<<endl;}; 
}; 

class B: public A { 
    public: 
    void f() { cout<<"b.f()"<<endl;}; 
}; 


int main(){ 

    B *b = new B(); 
    b->f(); 
    return 0; 
} 

Répondre

24

Redéfinir une fonction non-virtuelle est correct tant que vous ne dépendez pas du comportement de répartition virtuelle.

L'auteur du livre a peur que vous passiez votre B* à une fonction qui prend un A* et que vous soyez contrarié lorsque le résultat est un appel à la méthode de base et non à la méthode dérivée.

+2

Belle réponse! Bref, au point, et montre à quel point la programmation émotionnelle est. – DarenW

+0

Je ne suis pas d'accord, l'auteur Scott Meyers souligne que l'héritage public établit un invariant par rapport à la spécialisation pour la classe b. De plus, l'utilisation des classes est source de confusion lorsque le comportement du comportement f() dépend de la définition du pointeur et non de la définition de l'objet. Exemple: B x; A * ptr = & x; ptr-> f() // appelle la version de classe A de f() et non la version B de f() et cela prête à confusion. – TheChrisONeil

7

Essayez ceci:

int main(){ 
    A *b = new B(); 
    b->f(); 
    return 0; 
} 

Je pense que la réponse sera évidente une fois que vous voyez le résultat ;-).

Sans être virtuel, le mécanisme de liaison tardive ne sera pas utilisé, par conséquent la fonction définie pour ce type de pointeur sera utilisée, pas la fonction liée tardivement que veut appeler. Cela conduit à des tonnes de bugs mal traçables. Par conséquent, ce que vous faites est de créer une nouvelle fonction. Il peut être être ce que vous vouliez, mais quelqu'un lisant votre code par la suite pourrait s'attendre à ce que le code ci-dessus fonctionne avec la liaison tardive. Très perturbant.

L'une des caractéristiques que je voulais vraiment voir est un avertissement dans ce cas, avec un mot-clé pour l'empêcher, mais les rêves sont des rêves et la réalité est la réalité -_-

2

Le point « redéfinir » est Si, par exemple, vous avez une liste de pointeurs vers la classe de base (List<A *> list) et que vous appelez f(), la méthode re-implémentée dans B ne sera pas appelée.