Comment appeler une fonction de classe parent à partir d'une fonction de classe dérivée?


604

Comment appeler la fonction parent à partir d'une classe dérivée à l'aide de C ++? Par exemple, j'ai une classe appelée parentet une classe appelée childqui est dérivée du parent. Dans chaque classe, il y a une printfonction. Dans la définition de la fonction d'impression de l'enfant, je voudrais appeler la fonction d'impression des parents. Comment pourrais-je procéder?


Toutes les solutions ci-dessus supposent que votre fonction d'impression est une méthode statique. Est-ce le cas? Si la méthode n'est pas statique, les solutions ci-dessus ne sont pas pertinentes.
hhafez

14
hhafez, vous vous trompez, la syntaxe base :: function () ressemble à la syntaxe d'appel de méthode statique mais elle fonctionne pour les méthodes d'instance dans ce contexte.
Motti

2
Je n'utiliserais pas le MSVC __super car il est spécifique à la plate-forme. Bien que votre code ne puisse s'exécuter sur aucune autre plate-forme, j'utiliserais les autres suggestions car elles le font comme le langage prévu.
Teaser du


L'antipattern où les classes dérivées sont toujours nécessaires pour appeler des fonctions de classe parent est Call super
Rufus

Réponses:


775

Je prendrai le risque de dire l'évidence: vous appelez la fonction, si elle est définie dans la classe de base, elle est automatiquement disponible dans la classe dérivée (sauf si elle l'est private).

S'il existe une fonction avec la même signature dans la classe dérivée, vous pouvez la désambiguïser en ajoutant le nom de la classe de base suivi de deux deux-points base_class::foo(...). Vous devez noter que contrairement à Java et C #, C ++ n'a pas de mot-clé pour "la classe de base" ( superou base) car C ++ prend en charge l' héritage multiple, ce qui peut conduire à une ambiguïté.

class left {
public:
    void foo();
};

class right {
public:
    void foo();
};

class bottom : public left, public right {
public:
    void foo()
    {
        //base::foo();// ambiguous
        left::foo();
        right::foo();

        // and when foo() is not called for 'this':
        bottom b;
        b.left::foo();  // calls b.foo() from 'left'
        b.right::foo();  // call b.foo() from 'right'
    }
};

Par ailleurs, vous ne pouvez pas dériver directement de la même classe deux fois car il n'y aura aucun moyen de faire référence à l'une des classes de base par rapport à l'autre.

class bottom : public left, public left { // Illegal
};

31
Pourquoi voudriez-vous hériter deux fois de la même classe?
Paul Brewczynski

65
@bluesm: dans la POO classique, cela n'a pas beaucoup de sens, mais dans la programmation générique, template<class A, class B> class C: public A, public B {};deux types peuvent être identiques pour des raisons dépendant de la façon dont votre code est utilisé (ce qui rend A et B identiques), peut être deux ou trois couche d'abstraction façon de quelqu'un qui ne sait pas ce que vous avez fait.
Emilio Garavaglia

8
Je pense qu'il est utile d'ajouter que cela appellera la méthode de classe parent même si elle n'est pas implémentée directement dans la classe parent, mais est implémentée dans l'une des classes parent de la chaîne d'héritage.
Maxim Lavrov du

4
Sur un sidenote, cela m'a rendu fou quand j'ai essayé de le mettre dans un fichier cpp. J'avais «utiliser l'espace de noms std». «gauche» est défini quelque part dans cet espace de noms. L'exemple ne compilerait pas - m'a rendu fou :). Ensuite, j'ai changé «gauche» en «gauche». Excellent exemple d'ailleurs.
Mathai

72
@Mathai Et c'est pourquoi vous n'êtes pas censé utiliser using namespace std.
JAB

193

Étant donné la classe parent nommée Parentet la classe enfant nommée Child, vous pouvez faire quelque chose comme ceci:

class Parent {
public:
    virtual void print(int x);
}

class Child : public Parent {
    void print(int x) override;
}

void Parent::print(int x) {
    // some default behavior
}

void Child::print(int x) {
    // use Parent's print method; implicitly passes 'this' to Parent::print
    Parent::print(x);
}

Notez qu'il Parents'agit du nom réel de la classe et non d'un mot clé.


Bien sûr, cela ne serait utile que si l'appel de base était entrecoupé d'une autre logique, sinon il n'y aurait pas de raison de remplacer la fonction, alors c'est peut-être un peu trop pertinent ;)
underscore_d

1
@underscore_d en fait, c'est utile même si l'appel de base n'a pas été entrecoupé d'une autre logique. Disons que la classe parent fait à peu près tout ce que vous voulez, mais expose une méthode foo () que vous ne voulez pas que les utilisateurs de l'enfant utilisent - soit parce que foo () n'a pas de sens chez l'enfant ou que les appelants externes à l'enfant vont bousiller ce qu'est l'enfant Faire. Ainsi, l'enfant peut utiliser parent :: foo () dans certaines situations mais fournir une implémentation de foo afin de masquer foo () du parent d'être appelé.
iheanyi

@iheanyi Cela semble intéressant, mais désolé, je ne le saisis pas encore. Est-ce foo()ici print()une fonction analogue ou distincte? Et voulez - vous dire en utilisant l' privatehéritage pour cacher les détails hérités de la base, et en fournissant publicshadowing fonctions pour des choses que vous ne souhaitez exposer?
underscore_d

@underscore_d Oui, foo()c'était analogue à print(). Permettez-moi de revenir à l'utilisation print()car je pense que cela aurait plus de sens dans ce contexte. Disons que quelqu'un a créé une classe qui a effectué un ensemble d'opérations sur un type de données particulier, a exposé certains accesseurs et a eu une print(obj&)méthode. J'ai besoin d'une nouvelle classe qui fonctionne array-of-objmais tout le reste est le même. La composition entraîne beaucoup de code dupliqué. L'héritage minimise cela, dans les print(array-of-obj&) appels en boucle print(obj&), mais ne veut pas que les clients appellent print(obj&)car cela n'a pas de sens pour eux de le faire
iheanyi

@underscore_d Ceci est basé sur l'hypothèse que je ne peux pas refactoriser les parties communes de la classe parent d'origine ou que le faire est incroyablement coûteux. L'héritage privé pourrait fonctionner, mais vous perdriez alors les accesseurs publics sur lesquels vous comptiez - et vous auriez donc besoin de dupliquer du code.
iheanyi

32

Si votre classe de base est appelée Baseet que votre fonction est appelée, FooBar()vous pouvez l'appeler directement à l'aide deBase::FooBar()

void Base::FooBar()
{
   printf("in Base\n");
}

void ChildOfBase::FooBar()
{
  Base::FooBar();
}

28

Dans MSVC, il existe un mot clé spécifique à Microsoft pour cela: __super


MSDN: vous permet d'indiquer explicitement que vous appelez une implémentation de classe de base pour une fonction que vous remplacez.

// deriv_super.cpp
// compile with: /c
struct B1 {
   void mf(int) {}
};

struct B2 {
   void mf(short) {}

   void mf(char) {}
};

struct D : B1, B2 {
   void mf(short) {
      __super::mf(1);   // Calls B1::mf(int)
      __super::mf('s');   // Calls B2::mf(char)
   }
};


5
Eh, je préfère typdefle parent comme quelque chose super.
Thomas Eding,

26
Je n'essaierai pas de justifier l'utilisation de __super; Je l'ai mentionné ici comme suggestion alternative. Les développeurs doivent connaître leur compilateur et comprendre les avantages et les inconvénients de ses capacités.
Andrey

13
Je préfère décourager quiconque de l'utiliser, car cela entrave gravement la portabilité du code.
Erbureth dit Réintégrer Monica

26
Je ne suis pas d'accord avec Andrey: les développeurs devraient connaître la norme et ne devraient pas avoir à se soucier des fonctionnalités du compilateur, si nous envisageons d'écrire un logiciel qui est principalement indépendant du compilateur, ce qui, je pense, est de toute façon une bonne idée car tôt ou tard dans les grands projets, plusieurs compilateurs sont de toute façon utilisés.
Gabriel

7
"Les développeurs doivent connaître leur compilateur" ce raisonnement, et l'inclusion de fonctionnalités non standard, est ce qui a conduit à IE6 ...
e2-e4

5

Si le modificateur d'accès de la fonction membre de la classe de base est protégé OU public, vous pouvez appeler la fonction membre de la classe de base à partir de la classe dérivée. L'appel à la fonction membre non virtuelle et virtuelle de la classe de base à partir de la fonction membre dérivée peut être effectué. Veuillez vous référer au programme.

#include<iostream>
using namespace std;

class Parent
{
  protected:
    virtual void fun(int i)
    {
      cout<<"Parent::fun functionality write here"<<endl;
    }
    void fun1(int i)
    {
      cout<<"Parent::fun1 functionality write here"<<endl;
    }
    void fun2()
    {

      cout<<"Parent::fun3 functionality write here"<<endl;
    }

};

class Child:public Parent
{
  public:
    virtual void fun(int i)
    {
      cout<<"Child::fun partial functionality write here"<<endl;
      Parent::fun(++i);
      Parent::fun2();
    }
    void fun1(int i)
    {
      cout<<"Child::fun1 partial functionality write here"<<endl;
      Parent::fun1(++i);
    }

};
int main()
{
   Child d1;
   d1.fun(1);
   d1.fun1(2);
   return 0;
}

Production:

$ g++ base_function_call_from_derived.cpp
$ ./a.out 
Child::fun partial functionality write here
Parent::fun functionality write here
Parent::fun3 functionality write here
Child::fun1 partial functionality write here
Parent::fun1 functionality write here

1
Merci d'avoir apporté quelques exemples avec virtual!
M.Ioan

5

Appelez la méthode parent avec l'opérateur de résolution d'étendue parent.

Parent :: method ()

class Primate {
public:
    void whatAmI(){
        cout << "I am of Primate order";
    }
};

class Human : public Primate{
public:
    void whatAmI(){
        cout << "I am of Human species";
    }
    void whatIsMyOrder(){
        Primate::whatAmI(); // <-- SCOPE RESOLUTION OPERATOR
    }
};

-15
struct a{
 int x;

 struct son{
  a* _parent;
  void test(){
   _parent->x=1; //success
  }
 }_son;

 }_a;

int main(){
 _a._son._parent=&_a;
 _a._son.test();
}

Exemple de référence.


2
Pourriez-vous s'il vous plaît modifier dans une explication de pourquoi / comment ce code répond à la question? Les réponses en code uniquement sont déconseillées, car elles ne sont pas aussi faciles à apprendre que le code avec une explication. Sans explication, il faut beaucoup plus de temps et d'efforts pour comprendre ce qui a été fait, les modifications apportées au code ou si le code est utile. L'explication est importante à la fois pour les personnes qui tentent d'apprendre de la réponse et ceux qui évaluent la réponse pour voir si elle est valide ou vaut la peine de voter.
Makyen

3
Cette réponse concerne les classes imbriquées alors que la question portait sur les classes dérivées (même si les mots «parent» et «enfant» sont un peu trompeurs) et ne répond donc pas du tout à la question.
Johannes Matokic
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.