Appelez la méthode de la classe de base à partir de l’object de la classe dérivée

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"<