Puis-je appeler la fonction virtuelle d'une classe de base si je la remplace?


340

Disons que j'ai des cours Fooet que Barj'installe comme ceci:

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
        std::cout << x << std::endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        // I would like to call Foo.printStuff() here...
        std::cout << y << std::endl;
    }
};

Comme annoté dans le code, j'aimerais pouvoir appeler la fonction de la classe de base que je remplace. En Java, il y a la super.funcname()syntaxe. Est-ce possible en C ++?



1
Pour les Googleurs: notez que vous pouvez avoir des problèmes comme je l'ai fait avec le stockage en tant que variable de membre de classe qui n'est pas un pointeur. Voir ma réponse ici: stackoverflow.com/questions/4798966/… J'ai impliqué de nouveaux / supprimer pour corriger.
Andrew

Réponses:


449

La syntaxe C ++ est comme ceci:

class Bar : public Foo {
  // ...

  void printStuff() {
    Foo::printStuff(); // calls base class' function
  }
};

11
Y a-t-il des problèmes possibles avec cela? Est-ce une mauvaise pratique?
Robben_Ford_Fan_boy

36
@David: Non, c'est parfaitement normal de le faire, bien que cela puisse dépendre de votre classe réelle si c'est réellement utile. N'appelez la méthode de classe de base que si quelque chose vous arrive;).
STH

20
attention c'est une odeur de code quand vos clients deviennent OBLIGATOIRES pour le faire! (appelé call super requirement, consultez-le ici: en.wikipedia.org/wiki/Call_super )
v.oddou

8
@ v.oddou: Il n'y a rien de mal à appeler la super classe quand c'est utile. Cette page que vous avez liée est à peu près un problème potentiel spécifique impliquant l'héritage. Il se dit même qu'il n'y a rien de mal à appeler la super classe en général: "Notez que c'est l' exigence d'appeler le parent qui est l'anti-modèle. Il existe de nombreux exemples dans le code réel où la méthode dans la sous-classe peut encore veulent la fonctionnalité de la superclasse, généralement là où elle ne fait qu'augmenter la fonctionnalité parent. "
STH

26
Cela peut être évident pour la plupart, mais pour être complet, n'oubliez pas de ne jamais faire cela chez les constructeurs et les destructeurs.
TigerCoding

123

Oui,

class Bar : public Foo
{
    ...

    void printStuff()
    {
        Foo::printStuff();
    }
};

C'est la même chose qu'en superJava, sauf qu'il permet d'appeler des implémentations à partir de différentes bases lorsque vous avez plusieurs héritages.

class Foo {
public:
    virtual void foo() {
        ...
    }
};

class Baz {
public:
    virtual void foo() {
        ...
    }
};

class Bar : public Foo, public Baz {
public:
    virtual void foo() {
        // Choose one, or even call both if you need to.
        Foo::foo();
        Baz::foo();
    }
};

7
C'est une meilleure réponse que celle sélectionnée. Merci.
Mad Physicist

Héritage multiple? Ooh ouais!
lamino

69

Parfois, vous devez appeler l'implémentation de la classe de base, lorsque vous n'êtes pas dans la fonction dérivée ... Cela fonctionne toujours:

struct Base
{
    virtual int Foo()
    {
        return -1;
    }
};

struct Derived : public Base
{
    virtual int Foo()
    {
        return -2;
    }
};

int main(int argc, char* argv[])
{
    Base *x = new Derived;

    ASSERT(-2 == x->Foo());

    //syntax is trippy but it works
    ASSERT(-1 == x->Base::Foo());

    return 0;
}

2
Notez que vous devez à la fois xêtre de type Base*et utiliser la Base::Foosyntaxe pour que cela fonctionne
TTimo

27

Juste au cas où vous le feriez pour beaucoup de fonctions dans votre classe:

class Foo {
public:
  virtual void f1() {
    // ...
  }
  virtual void f2() {
    // ...
  }
  //...
};

class Bar : public Foo {
private:
  typedef Foo super;
public:
  void f1() {
    super::f1();
  }
};

Cela pourrait économiser un peu d'écriture si vous souhaitez renommer Foo.


1
Une façon amusante de le faire, mais ne fonctionnera pas avec l'héritage multiple.
Mad Physicist

5
Et si vous avez plus d'une classe de base? Quoi qu'il en soit, il est stupide et souvent futile d'essayer de plier C ++ pour ressembler à un autre langage. N'oubliez pas le nom de la base et appelez-le par cela.
underscore_d

6

Si vous souhaitez appeler une fonction de la classe de base à partir de sa classe dérivée, vous pouvez simplement appeler à l'intérieur de la fonction remplacée en mentionnant le nom de la classe de base (comme Foo :: printStuff () ).

le code va ici

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
        Foo::printStuff();/////also called the base class method
    }
};

int main()
{
    Bar *b=new Bar;
    b->printStuff();
}

Encore une fois, vous pouvez déterminer à l'exécution quelle fonction appeler en utilisant l'objet de cette classe (dérivée ou de base) .Mais cela nécessite que votre fonction à la classe de base soit marquée comme virtuelle.

code ci-dessous

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
    }
};

int main()
{

    Foo *foo=new Foo;
    foo->printStuff();/////this call the base function
    foo=new Bar;
    foo->printStuff();
}

0

vérifie ça...

#include <stdio.h>

class Base {
public:
   virtual void gogo(int a) { printf(" Base :: gogo (int) \n"); };    
   virtual void gogo1(int a) { printf(" Base :: gogo1 (int) \n"); };
   void gogo2(int a) { printf(" Base :: gogo2 (int) \n"); };    
   void gogo3(int a) { printf(" Base :: gogo3 (int) \n"); };
};

class Derived : protected Base {
public:
   virtual void gogo(int a) { printf(" Derived :: gogo (int) \n"); };
   void gogo1(int a) { printf(" Derived :: gogo1 (int) \n"); };
   virtual void gogo2(int a) { printf(" Derived :: gogo2 (int) \n"); };
   void gogo3(int a) { printf(" Derived :: gogo3 (int) \n"); };       
};

int main() {
   std::cout << "Derived" << std::endl;
   auto obj = new Derived ;
   obj->gogo(7);
   obj->gogo1(7);
   obj->gogo2(7);
   obj->gogo3(7);
   std::cout << "Base" << std::endl;
   auto base = (Base*)obj;
   base->gogo(7);
   base->gogo1(7);
   base->gogo2(7);
   base->gogo3(7);

   std::string s;
   std::cout << "press any key to exit" << std::endl;
   std::cin >> s;
   return 0;
}

production

Derived
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Derived :: gogo2 (int)
 Derived :: gogo3 (int)
Base
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Base :: gogo2 (int)
 Base :: gogo3 (int)
press any key to exit

la meilleure façon est d'utiliser la fonction base :: comme disent @sth


Comme expliqué dans cette question , cela ne devrait pas fonctionner en raison de l' protectedhéritage. Pour transtyper un pointeur de classe de base, vous devez utiliser l'héritage public.
Darkproduct

Intéressant. Après avoir lu cette réponse, j'ai pensé qu'avec l'héritage protégé, le fait que Derived est dérivé de Base ne serait visible que pour la classe elle-même et non visible pour l'extérieur aussi.
Darkproduct

0

Oui, vous pouvez l'appeler. La syntaxe C ++ pour appeler la fonction de classe parent dans la classe enfant est

class child: public parent {
  // ...

  void methodName() {
    parent::methodName(); // calls Parent class' function
  }
};

En savoir plus sur la fonction prioritaire .

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.