web-dev-qa-db-fra.com

Appel de la méthode de classe de base à partir d'un objet de classe dérivé

Comment puis-je appeler une méthode de classe de base qui est remplacée par la classe dérivée, à partir d'un objet 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;
}
35
Xun Yang

Vous pouvez toujours (*) faire référence à la fonction d'une classe de base en utilisant un id-qualifié :

#include <iostream>

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 également de quelques fautes de frappe de l'OP.]

(*) Des restrictions d'accès 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 contraire, Derived::foo masque 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, tant que sa signature est compatible avec Base::bar.)

Un id-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 le premier formulaire, nous recherchons X, puis nous recherchons Y dans le contexte de X. Dans le second formulaire, nous recherchons Y dans l'espace de noms global.

Un unqualified-id ne contient pas de ::, Et ne spécifie donc pas (lui-même) un contexte où rechercher le nom.

Dans une expression b->foo, b et foo sont des identifiants 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 accès membre de classe, nous recherchons foo dans le contexte du type de b (ou plutôt *b). Nous recherchons donc foo dans le contexte de Base. Nous trouverons la fonction membre void foo() déclarée dans Base, que j'appellerai Base::foo.

Pour foo, nous avons terminé maintenant et appelons Base::foo.

Pour b->bar, Nous trouvons d'abord Base::bar, Mais il est déclaré virtual. Parce qu'il s'agit de virtual, nous effectuons une répartition virtuelle . Cela appellera le remplacement de fonction final dans la hiérarchie des classes du type de l'objet auquel pointe b. Étant donné que b pointe vers un objet de type Derived, la substitution finale est Derived::bar.

En recherchant le nom foo dans le contexte de Derived, nous trouverons Derived::foo. C'est pourquoi Derived::foo Est censé masquer Base::foo. Les expressions telles que d.foo() ou, à l'intérieur d'une fonction membre de Derived, en utilisant simplement foo() ou this->foo(), rechercheront à partir du contexte de Derived.

Lorsque vous utilisez un id-qualifié , nous indiquons explicitement le contexte de la recherche d'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 a hérité, par exemple) . De plus, 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 la répartition virtuelle, car ils doivent être remplacés. Cependant, vous pouvez toujours appeler leur implémentation (s'ils en ont une) en utilisant un id-qualifié .

#include <iostream>

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 spécificateurs d'accès à la fois des membres de la classe et des classes de base ont une influence sur l'utilisation ou non d'un identifiant qualifié pour appeler la fonction d'une classe de base sur un objet d'un type dérivé.

Par exemple:

#include <iostream>

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. Le masquage de nom n'a donc aucune influence sur celui-ci (vous pouvez omettre 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 qualifiés-id).

L'erreur dans p.Base::private_fun() est d'ailleurs différente de l'erreur dans r.Base::public_fun(): la première ne fait déjà pas référence au nom Base::private_fun (Car c'est un nom privé) . Le second ne parvient pas à convertir r de Private_derived& En Base& Pour le pointeur this- (essentiellement). C'est pourquoi le second fonctionne à partir de Private_derived Ou d'un ami de Private_derived.

58
dyp

Tout d'abord, Derived devrait 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;
}

Ensuite, vous pouvez faire appel à Derived :: foo 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 l'ID qualifié de Base :: foo

 int main(){
    Derived bar;
    bar.Base::foo(); // calls Base::foo()
    return 0;
 }
12
user995502

Pensez à rendre foo() virtuel en premier lieu.

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;
}
3
user142019

Une note [supplémentaire] importante : vous aurez toujours des erreurs de compilation si Le nom se cache se produit.

Dans ce cas, utilisez le mot clé using ou utilisez le qualifer. De plus, voir cette réponse également.

#include <iostream>

class Base{
  public:
    void foo(bool bOne, bool bTwo){std::cout<<"base"<<bOne<<bTwo;}
};

class Derived : public Base
{
  public:
    void foo(bool bOne){std::cout<<"derived"<<bOne;}
};

int main()
{
  Derived bar;
  //bar.foo(true,true);      // error:    derived func attempted
  bar.foo(true);             // no error: derived func
  bar.Base::foo(true,true);  // no error: base func, qualified
  return 0;
}
0
bunkerdive