Considérez le code:
#include <stdio.h>
class Base {
public:
virtual void gogo(int a){
printf(" Base :: gogo (int) \n");
};
virtual void gogo(int* a){
printf(" Base :: gogo (int*) \n");
};
};
class Derived : public Base{
public:
virtual void gogo(int* a){
printf(" Derived :: gogo (int*) \n");
};
};
int main(){
Derived obj;
obj.gogo(7);
}
Vous avez cette erreur:
> g ++ -pedantic -Os test.cpp -o test test.cpp: Dans la fonction `int main () ': test.cpp: 31: erreur: pas de fonction correspondante pour l'appel à `Derived :: gogo (int) ' test.cpp: 21: note: les candidats sont: virtual void Derived :: gogo (int *) test.cpp: 33: 2: avertissement: pas de nouvelle ligne à la fin du fichier > Code de sortie: 1
Ici, la fonction de la classe dérivée éclipse toutes les fonctions de même nom (pas de signature) dans la classe de base. D'une manière ou d'une autre, ce comportement de C ++ ne semble pas correct. Pas polymorphe.
c++
polymorphism
overriding
Aman Aggarwal
la source
la source
obj.Base::gogo(7);
fonctionne toujours en appelant la fonction cachée.Réponses:
À en juger par le libellé de votre question (vous avez utilisé le mot «cacher»), vous savez déjà ce qui se passe ici. Le phénomène est appelé "dissimulation de nom". Pour une raison quelconque, chaque fois que quelqu'un pose une question sur la raison pour laquelle le masquage de nom se produit, les personnes qui répondent disent que cela s'appelle "masquage de nom" et expliquent comment cela fonctionne (que vous connaissez probablement déjà), ou expliquent comment le remplacer (ce que vous n'a jamais posé de questions), mais personne ne semble se soucier de répondre à la véritable question du «pourquoi».
La décision, la raison d'être du nom qui se cache, c'est-à-dire pourquoi il a été conçu en C ++, est d'éviter certains comportements contre-intuitifs, imprévus et potentiellement dangereux qui pourraient se produire si l'ensemble hérité de fonctions surchargées était autorisé à se mélanger avec l'ensemble actuel de surcharges dans la classe donnée. Vous savez probablement que la résolution de surcharge en C ++ fonctionne en choisissant la meilleure fonction dans l'ensemble des candidats. Cela se fait en faisant correspondre les types d'arguments aux types de paramètres. Les règles de correspondance peuvent parfois être compliquées et conduire souvent à des résultats qui peuvent être perçus comme illogiques par un utilisateur non préparé. L'ajout de nouvelles fonctions à un ensemble de fonctions existantes peut entraîner un changement assez radical des résultats de résolution de surcharge.
Par exemple, supposons que la classe de base
B
possède une fonction membrefoo
qui accepte un paramètre de typevoid *
, et tous les appels àfoo(NULL)
sont résolus enB::foo(void *)
. Disons qu'il n'y a pas de nom caché et celaB::foo(void *)
est visible dans de nombreuses classes différentes qui descendentB
. Cependant, disons que dans un descendantD
de classe [indirect, distant],B
une fonctionfoo(int)
est définie. Désormais, sans nom, le masquageD
a à la foisfoo(void *)
etfoo(int)
visible et participe à la résolution de surcharge. À quelle fonction les appels seront-ilsfoo(NULL)
résolus s'ils sont effectués via un objet de typeD
? Ils se résoudront àD::foo(int)
, puisqu'ilint
s'agit d'une meilleure correspondance pour le zéro intégral (c.-à-d.NULL
) que tout type de pointeur. Ainsi, tout au long de la hiérarchie, les appels à sefoo(NULL)
résoudre à une fonction, tandis que dansD
(et sous) ils se résolvent soudainement à une autre.Un autre exemple est donné dans La conception et l'évolution de C ++ , page 77:
Sans cette règle, l'état de b serait partiellement mis à jour, entraînant un découpage.
Ce comportement a été jugé indésirable lors de la conception du langage. Comme meilleure approche, il a été décidé de suivre la spécification "masquage de nom", ce qui signifie que chaque classe commence par une "feuille blanche" en ce qui concerne chaque nom de méthode qu'elle déclare. Afin de remplacer ce comportement, une action explicite est requise de la part de l'utilisateur: à l'origine une redéclaration de méthode (s) héritée (actuellement déconseillée), maintenant une utilisation explicite de using-declaration.
Comme vous l'avez correctement observé dans votre message d'origine (je fais référence à la remarque "non polymorphe"), ce comportement peut être considéré comme une violation de la relation IS-A entre les classes. C'est vrai, mais apparemment à l'époque, il a été décidé qu'à la fin, la dissimulation du nom se révélerait être un moindre mal.
la source
nullptr
Je vais maintenant m'opposer à votre exemple en disant "si vous voulez appeler lavoid*
version, vous devez utiliser un type de pointeur". Y a-t-il un exemple différent où cela peut être mauvais?d->foo()
- être ne vous obtiendrez pas le "Is-aBase
", mais lestatic_cast<Base*>(d)->foo()
fera , y compris l'envoi dynamique.Les règles de résolution de nom indiquent que la recherche de nom s'arrête dans la première étendue dans laquelle un nom correspondant est trouvé. À ce stade, les règles de résolution de surcharge interviennent pour trouver la meilleure correspondance des fonctions disponibles.
Dans ce cas,
gogo(int*)
se trouve (seul) dans la portée de la classe dérivée, et comme il n'y a pas de conversion standard d'int en int *, la recherche échoue.La solution consiste à introduire les déclarations de base via une déclaration using dans la classe Derived:
... permettrait aux règles de recherche de nom de trouver tous les candidats et donc la résolution de surcharge se déroulerait comme prévu.
la source
C'est "By Design". En C ++, la résolution de surcharge pour ce type de méthode fonctionne comme suit.
Étant donné que Derived n'a pas de fonction correspondante nommée "gogo", la résolution de surcharge échoue.
la source
Le masquage de nom est logique car il empêche les ambiguïtés dans la résolution de nom.
Considérez ce code:
Si
Base::func(float)
n'était pas caché parDerived::func(double)
dans Derived, nous appellerions la fonction de classe de base lors de l'appeldobj.func(0.f)
, même si un flottant peut être promu en double.Référence: http://bastian.rieck.ru/blog/posts/2016/name_hiding_cxx/
la source