À en juger par la formulation de votre question (vous avez utilisé le mot "cacher"), vous savez déjà ce qui se passe ici. Le phénomène s'appelle "masquage de nom". Pour une raison quelconque, chaque fois que quelqu'un pose une question sur pourquoi le masquage de nom se produit, les personnes qui répondent disent soit que cela s'appelle "masquage de nom" et expliquent comment cela fonctionne (que vous connaissez probablement déjà), soit expliquent comment le remplacer (ce qui n'était pas votre question), mais personne ne semble se soucier de répondre à la question réelle "pourquoi".
La décision, la justification derrière le masquage de nom, c'est-à-dire pourquoi il a réellement é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 en C++, la résolution de surcharge fonctionne en choisissant la meilleure fonction parmi l'ensemble de candidats. Cela se fait en faisant correspondre les types d'arguments aux types de paramètres. Les règles de correspondance peuvent être compliquées parfois, et conduisent souvent à des résultats qui pourraient être perçus comme illogiques par un utilisateur non préparé. Ajouter de nouvelles fonctions à un ensemble de fonctions déjà existantes pourrait entraîner un changement assez drastique dans les résultats de la résolution de surcharge.
Par exemple, disons que la classe de base B
a une fonction membre foo
qui prend un paramètre de type void *
, et que tous les appels à foo(NULL)
sont résolus en B::foo(void *)
. Supposons qu'il n'y a pas de masquage de nom et que ce B::foo(void *)
est visible dans de nombreuses classes descendantes de B
. Cependant, disons que dans une [descendance indirecte, distante] D
de la classe B
, une fonction foo(int)
est définie. Maintenant, sans masquage de nom, D
a à la fois foo(void *)
et foo(int)
visibles et participant à la résolution de surcharge. Vers quelle fonction les appels à foo(NULL)
se résoudront-ils, s'ils sont faits via un objet de type D
? Ils seront résolus en D::foo(int)
, puisque int
est un meilleur correspondant pour zéro entier (c'est-à-dire NULL
) que tout type de pointeur. Ainsi, dans toute la hiérarchie, les appels à foo(NULL)
se résolvent en une fonction, tandis que dans D
(et en dessous), ils se résolvent soudainement en une autre.
Un autre exemple est donné dans The Design and Evolution of C++, page 77:
class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};
class Derived : public Base{
int xx;
public:
virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};
void f(Base a, Derived b)
{
a.copy(&b); // ok: copie la partie Base de b
b.copy(&a); // erreur: copie(Base*) est masquée par copy(Derived*)
}
Sans cette règle, l'état de b serait partiellement mis à jour, entraînant une coupe.
Ce comportement a été jugé indésirable lors de la conception du langage. En tant qu'approche meilleure, il a été décidé de suivre la spécification du "masquage de nom", signifiant que chaque classe commence avec une "feuille blanche" par rapport à chaque nom de méthode qu'elle déclare. Pour annuler ce comportement, une action explicite de l'utilisateur est requise : initialement une redéclaration de méthode(s) héritée(s) (actuellement déconseillée), maintenant une utilisation explicite de la déclaration using.
Comme vous l'avez correctement observé dans votre publication originale (je fais référence à la remarque "Non polymorphe"), ce comportement pourrait ê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'en fin de compte, le masquage de nom s'avérerait être un moindre mal.
1 votes
Duplicate : stackoverflow.com/questions/411103/…
10 votes
Brillante question, je viens tout juste de découvrir cela récemment aussi
11 votes
Je pense que Bjarne (du lien posté par Mac) l'a mieux exprimé en une phrase : "En C++, il n'y a pas de surcharge entre les espaces de noms - les espaces de noms de classes dérivées ne font pas exception à cette règle générale."
7 votes
@Ashish Ce lien est cassé. Voici le bon lien (pour l'instant) - stroustrup.com/bs_faq2.html#overloadderived
0 votes
Si vous êtes sûr que cela ne nuit en rien sans masquer les fonctions gogo de la classe de base, vous pouvez éviter de les masquer en ajoutant
using Base::gogo
dans la classe dérivée.3 votes
De plus, je voulais souligner que
obj.Base::gogo(7);
fonctionne toujours en appelant la fonction cachée.