Pourquoi un substituée fonction dans la classe dérivée cacher d'autres surcharges de la classe de base?
Examiner 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);
}
Eu cette erreur :
>g++ -pedantic -Os test.cpp -o test test.cpp: In function `int main()': test.rpc:31: error: no matching function for call to `Dérivée::gogo(int)' test.rpc:21: remarque: les candidats sont: virtual void Dérivés::gogo(int*) test.rpc:33:2: attention: pas de saut de ligne en fin de fichier >le code de Sortie: 1
Ici, la classe Dérivée de la fonction est éclipsant toutes les fonctions de même nom (pas de signature) dans la classe de base. En quelque sorte, ce comportement de C++ n'a pas l'air OK. Pas polymorphes.
- Dupliquer: stackoverflow.com/questions/411103/...
- brillant question, je n'ai découvert cela récemment trop
- Je pense que Bjarne (à partir du lien Mac posté) celui qui a le mieux en une phrase: "En C++, il n'y a pas de surcharge dans les étendues de la classe dérivée étendues ne sont pas une exception à cette règle générale."
- Que le lien est brisé. Voici le bon (comme maintenant) - stroustrup.com/bs_faq2.html#overloadderived
- Si tu sûr, il ne veut pas nuire à quoi que ce soit sans cacher la classe de base gogo fonctions, vous pouvez éviter de le cacher en ajoutant
using Base::gogo
dans la classe Dérivée. - Aussi, je voulais souligner que
obj.Base::gogo(7);
fonctionne toujours par l'appel de la fonction cachée.
Vous devez vous connecter pour publier un commentaire.
À en juger par la formulation de votre question (que vous avez utilisé le mot "hide"), vous savez déjà ce qui se passe ici. Le phénomène est appelé "nom de la clandestinité". Pour une raison quelconque, chaque fois que quelqu'un pose une question à propos de pourquoi nom de cacher qui se passe, les gens qui répondent dire que ce qu'on appelle "nom de la clandestinité et expliquer comment cela fonctionne (qui vous savez probablement déjà), ou d'expliquer comment le remplacer (ce qui vous jamais demandé à ce sujet), mais personne ne semble se soucier de répondre aux "pourquoi" question.
La décision, le raisonnement derrière le nom de la clandestinité, c'est à dire pourquoi il en fait a été conçu en C++, est d'éviter certains contre-intuitif, imprévisibles et potentiellement dangereux d'un comportement qui pourraient avoir lieu si les hérité d'un ensemble de fonctions surchargées ont été autorisés à mélanger avec de l'actuel ensemble de surcharges dans la classe donnée. Vous le savez probablement qu'en C++, la résolution de surcharge fonctionne en choisissant la meilleure fonction à partir de l'ensemble des candidats. Cela se fait en faisant correspondre les types des arguments de types de paramètres. Les règles de correspondance pourrait être compliqué à la fois, et conduisent souvent à des résultats qui pourraient être perçus comme illogique par un dépourvu de l'utilisateur. L'ajout de nouvelles fonctions à un ensemble de déjà existants pourraient résulter plutôt un changement radical dans la résolution de surcharge résultats.
Par exemple, disons que la classe de base
B
a une fonction membrefoo
qui prend un paramètre de typevoid *
, et tous les appels àfoo(NULL)
sont résolus àB::foo(void *)
. Disons qu'il n'y a aucun nom de la clandestinité et de ceB::foo(void *)
est visible dans beaucoup de différentes classes descendantes deB
. Cependant, disons que dans certains [indirecte, à distance] descendantD
de classeB
une fonctionfoo(int)
est défini. Maintenant, sans nom de cacherD
a deuxfoo(void *)
etfoo(int)
visible et en participant à la résolution de surcharge. Quelle fonction les appels àfoo(NULL)
résoudre à, si elle est faite par l'intermédiaire d'un objet de typeD
? Ils vont résoudre àD::foo(int)
, depuisint
est une meilleure correspondance intégrale de zéro (c'est à direNULL
) que n'importe quel type de pointeur. Ainsi, tout au long de la hiérarchie des appels àfoo(NULL)
résoudre à une seule fonction, tandis que dansD
(et moins) ils ont soudainement résoudre à un autre.Un autre exemple est donné dans La Conception et l'Évolution de C++, page 77:
Sans cette règle, b l'état serait partiellement mis à jour, conduisant à découpage.
Ce comportement a été jugé indésirable quand le langage a été conçu. Une meilleure approche, il a été décidé de suivre le nom "cacher" de la spécification, ce qui signifie que chaque classe commence par un "clean sheet" à l'égard de chaque nom de la méthode, il déclare. Afin de tenir compte de ce comportement, une action explicite est requis de l'utilisateur: à l'origine un redeclaration de la méthode héritée(s) (actuellement obsolète), et maintenant une utilisation explicite de l'utilisation de la déclaration.
Comme vous l'avez observé dans votre post original (je fais référence à la "Pas polymorphe" la remarque), ce comportement peut être considéré comme une violation de l'EST-UN relationsip entre les classes. C'est vrai, mais apparemment, à l'époque, il avait été décidé qu'à la fin du nom de la clandestinité se révélera être un moindre mal.
nullptr
je voudrais objet de votre exemple en disant "si tu voulais appeler levoid*
version, vous devez utiliser un pointeur de type". Est-il un autre exemple où cela peut être mauvais?d->foo()
vous n'obtiendrez pas de "Est-unBase
", maisstatic_cast<Base*>(d)->foo()
est, y compris la dynamique de l'expédition.int n = pow(2,4)
, j'ai une erreur de compilation, en disant que cet appel est ambiguë et peut être assorti àT pow(T,T)
pourT=double
T=float
ouT=long
. Comment se fait l'un comportement indésirable justifiant nom de cacher (c'est à direfoo(NULL)
peut être jumelé àfoo(void *)
oufoo(int)
) n'est pas géré par une telle ambiguë "appel" erreur de compilation ?pow
exemple, l'erreur est générée, car il n'y a pas de correspondance exacte pour votre argumentation. Dansfoo(NULL)
cas avecNULL
défini comme0
, leint
version offre une correspondance exacte et aucune erreur n'est générée.Les règles de résolution de noms de dire que la recherche d'un nom, s'arrête dans le premier domaine dans lequel un nom correspondant est trouvé. À ce point, la résolution de surcharge règles de coup de pied dans le pour trouver la meilleure correspondance entre les fonctions disponibles.
Dans ce cas,
gogo(int*)
est trouvé (seul) dans la classe Dérivée de la portée, et comme il n'y a pas de standard de conversion de int int*, la recherche échoue.La solution est d'importer la Base de déclarations par l'intermédiaire d'une aide de la déclaration dans la classe Dérivée:
...permettrait au nom de règles de recherche pour trouver tous les candidats et donc la résolution de surcharge, de procéder comme vous l'avez prévu.
C'est "By Design". En C++ résolution de surcharge pour ce type de méthode fonctionne comme suit.
Depuis Dérivé ne pas correspondre une fonction nommée "gogo", résolution de surcharge échoue.
Nom de cacher un sens, car il empêche les ambiguïtés de la résolution de nom.
Considérer ce code:
Si
Base::func(float)
n'était pas cachée parDerived::func(double)
dans la Dérivée, on pourrait appeler la classe de base de la fonction lors de l'appel dedobj.func(0.f)
, même si un flotteur peut être promu à un double.Référence: http://bastian.rieck.ru/blog/posts/2016/name_hiding_cxx/