Comment puis-je appeler une méthode de classe de base qui est remplacée par la classe dérivée, à partir d’un object de classe dérivée?
class Base{ public: void foo(){cout<<"base";} }; class Derived:public Base{ public: void foo(){cout<<"derived";} } int main(){ Derived bar; //call Base::foo() from bar here? return 0; }
Vous pouvez toujours (*) faire référence à la fonction d’une classe de base à l’aide d’un identifiant qualifié :
#include class Base{ public: void foo(){std::cout<<"base";} }; class Derived : public Base { public: void foo(){std::cout<<"derived";} }; int main() { Derived bar; //call Base::foo() from bar here? bar.Base::foo(); // using a qualified-id return 0; }
[Correction de quelques fautes de frappe de l'OP.]
(*) Les ressortingctions d'access s'appliquent toujours et les classes de base peuvent être ambiguës.
Si Base::foo
n'est pas virtual
, alors Derived::foo
ne remplace pas Base::foo
. Au lieu de cela, Derived::foo
cache la Base::foo
. La différence peut être vue dans l'exemple suivant:
struct Base { void foo() { std::cout << "Base::foo\n"; } virtual void bar() { std::cout << "Base::bar\n"; } }; struct Derived : Base { void foo() { std::cout << "Derived::foo\n"; } virtual void bar() { std::cout << "Derived::bar\n"; } }; int main() { Derived d; Base* b = &d; b->foo(); // calls Base::foo b->bar(); // calls Derived::bar }
( Derived::bar
est implicitement virtuel, même si vous n'utilisez pas le mot clé virtual
, à condition que sa signature soit compatible avec Base::bar
.)
Un identifiant qualifié est de la forme X :: Y
ou simplement :: Y
La partie précédant le ::
spécifie où nous voulons rechercher l'identifiant Y
Dans la première forme, nous recherchons X
, puis Y
partir du contexte de X
Dans la seconde forme, nous recherchons Y
dans l’espace de noms global.
Un identificateur non qualifié ne contient pas un ::
et ne spécifie donc pas (lui-même) un contexte dans lequel rechercher le nom.
Dans une expression b->foo
, b
et foo
sont tous deux des identificateurs non qualifiés . b
est recherché dans le contexte actuel (qui dans l'exemple ci-dessus est la fonction main
). Nous trouvons la variable locale Base* b
. Parce que b->foo
a la forme d'un access membre, nous recherchons foo
dans le contexte du type de b
(ou plutôt *b
). Nous cherchons donc foo
dans le contexte de Base
. Nous allons trouver la fonction membre void foo()
déclarée dans Base
, que je nommerai Base::foo
.
Pour foo
, nous avons terminé, et appelons Base::foo
.
Pour b->bar
, nous trouvons d'abord Base::bar
, mais il est déclaré virtual
. Parce que c'est virtual
, nous effectuons un dispatch virtuel . Ceci appellera la dernière fonction surchargée dans la hiérarchie de classes du type de l'object b
. Étant donné que b
pointe vers un object de type Derived
, l’autorégulateur final est Derived::bar
.
En recherchant le nom foo
dans le contexte de Derived
, nous trouverons Derived::foo
. C'est pourquoi on dit que Derived::foo
cache Base::foo
. Des expressions telles que d.foo()
ou, dans une fonction membre de Derived
, en utilisant simplement foo()
ou this->foo()
, rechercheront dans le contexte de Derived
.
Lors de l'utilisation d'un identifiant qualifié , nous énonçons explicitement le contexte dans lequel rechercher un nom. L'expression Base::foo
indique que nous voulons rechercher le nom foo
dans le contexte de Base
(il peut trouver des fonctions dont Base
hérité, par exemple). En outre, il désactive la répartition virtuelle.
Par conséquent, d.Base::foo()
trouvera Base::foo
et l’appellera; d.Base::bar()
trouvera Base::bar
et l'appellera.
Fait amusant: les fonctions virtuelles pures peuvent avoir une implémentation. Ils ne peuvent pas être appelés via le dispatch virtuel, car ils doivent être remplacés. Toutefois, vous pouvez toujours appeler leur implémentation (s’ils en ont une) en utilisant un identifiant qualifié .
#include struct Base { virtual void foo() = 0; }; void Base::foo() { std::cout << "look ma, I'm pure virtual!\n"; } struct Derived : Base { virtual void foo() { std::cout << "Derived::foo\n"; } }; int main() { Derived d; d.foo(); // calls Derived::foo d.Base::foo(); // calls Base::foo }
Notez que les spécificateurs d'access des membres de la classe et des classes de base ont une influence sur le fait que vous puissiez ou non utiliser un identifiant qualifié pour appeler la fonction d'une classe de base sur un object d'un type dérivé.
Par exemple:
#include struct Base { public: void public_fun() { std::cout << "Base::public_fun\n"; } private: void private_fun() { std::cout << "Base::private_fun\n"; } }; struct Public_derived : public Base { public: void public_fun() { std::cout << "Public_derived::public_fun\n"; } void private_fun() { std::cout << "Public_derived::private_fun\n"; } }; struct Private_derived : private Base { public: void public_fun() { std::cout << "Private_derived::public_fun\n"; } void private_fun() { std::cout << "Private_derived::private_fun\n"; } }; int main() { Public_derived p; p.public_fun(); // allowed, calls Public_derived::public_fun p.private_fun(); // allowed, calls Public_derived::public_fun p.Base::public_fun(); // allowed, calls Base::public_fun p.Base::private_fun(); // NOT allowed, tries to name Base::public_fun Private_derived r; r.Base::public_fun(); // NOT allowed, tries to call Base::public_fun r.Base::private_fun(); // NOT allowed, tries to name Base::private_fun }
L'accessibilité est orthogonale à la recherche de nom. Ainsi, le masquage de nom n’a aucune influence sur celui-ci (vous pouvez public_fun
et private_fun
dans les classes dérivées et obtenir le même comportement et les mêmes erreurs pour les appels avec l’identificateur qualifié).
L'erreur dans p.Base::private_fun()
est différente de l'erreur dans r.Base::public_fun()
en passant: La première omet déjà de se référer au nom Base::private_fun
(car c'est un nom privé) . Le second ne parvient pas à convertir r
de Private_derived&
to Base&
pour le this
-pointer (essentiellement). C'est pourquoi le second fonctionne depuis Private_derived
ou un ami de Private_derived
.
Tout d’abord, Derived doit hériter de Base.
class Derived : public Base{
Cela dit
Tout d’abord, vous ne pouvez tout simplement pas avoir foo dans Derived
class Base{ public: void foo(){cout<<"base";} }; class Derived : public Base{ } int main(){ Derived bar; bar.foo() // calls Base::foo() return 0; }
Deuxièmement, vous pouvez créer Derived :: foo call Base :: foo.
class Base{ public: void foo(){cout<<"base";} }; class Derived : public Base{ public: void foo(){ Base::foo(); } ^^^^^^^^^^ } int main(){ Derived bar; bar.foo() // calls Base::foo() return 0; }
Troisièmement, vous pouvez utiliser un identifiant qualifié de Base :: foo
int main(){ Derived bar; bar.Base::foo(); // calls Base::foo() return 0; }
Envisagez tout d’abord de rendre foo()
virtuel.
class Base { public: virtual ~Base() = default; virtual void foo() { … } }; class Derived : public Base { public: virtual void foo() override { … } };
Cependant, cela fait le travail:
int main() { Derived bar; bar.Base::foo(); return 0; }
Une remarque [supplémentaire] importante : vous aurez toujours des erreurs de compilation si le masquage de nom se produit.
Dans ce cas, utilisez le mot-clé using ou utilisez le qualifer. De plus, voir aussi cette réponse .
#include class Base{ public: void foo(bool bOne, bool bTwo){std::cout<<"base"<