Posso chiamare la funzione virtuale di una classe base se la sto sovrascrivendo?


340

Supponiamo che io abbia delle lezioni Fooe che sia Barimpostato in questo modo:

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

Come annotato nel codice, vorrei essere in grado di chiamare la funzione della classe base che sto scavalcando. In Java c'è la super.funcname()sintassi. È possibile in C ++?



1
Per i googler: nota che potresti avere problemi come ho fatto con l'archiviazione come variabile membro della classe che non è un puntatore. Vedi la mia risposta qui: stackoverflow.com/questions/4798966/… Ho coinvolto new / delete per risolvere.
Andrew,

Risposte:


449

La sintassi C ++ è così:

class Bar : public Foo {
  // ...

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

11
C'è qualche possibile problema nel fare questo? È una cattiva pratica?
Robben_Ford_Fan_boy,

36
@ David: No, è perfettamente normale farlo, anche se potrebbe essere utile se sei effettivamente utile. Chiama il metodo della classe base solo se fa qualcosa che vuoi che accada;).
qc

20
attenzione, questo è un odore di codice quando i tuoi clienti diventano NECESSARI per farlo! (chiamato call super requirement, dai un'occhiata qui: en.wikipedia.org/wiki/Call_super )
v.oddou

8
@ v.oddou: non c'è niente di sbagliato nel chiamare la super classe quando è utile. La pagina che hai collegato riguarda solo alcuni potenziali problemi specifici che riguardano l'eredità. Si dice anche che non c'è nulla di sbagliato nel chiamare la superclasse in generale: "Nota che è il requisito di chiamare il genitore che è l'anti-pattern. Ci sono molti esempi nel codice reale in cui il metodo nella sottoclasse può ancora desidera la funzionalità della superclasse, in genere laddove aumenta solo la funzionalità padre. "
qc

26
Può essere ovvio per la maggior parte, ma per completezza, ricorda di non farlo mai nei costruttori e nei distruttori.
TigerCoding

123

Sì,

class Bar : public Foo
{
    ...

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

È lo stesso superdi Java, tranne per il fatto che consente di chiamare implementazioni da basi diverse quando si dispone di ereditarietà multipla.

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
Questa è una risposta migliore di quella selezionata. Grazie.
Fisico pazzo,

Eredità multipla? Ooh yikes!
lamino,

69

A volte è necessario chiamare l'implementazione della classe base, quando non si è nella funzione derivata ... Funziona ancora:

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
Si noti che è necessario sia xper essere di tipo Base*che per utilizzare la Base::Foosintassi affinché ciò funzioni
TTimo

27

Nel caso in cui lo fai per molte funzioni della tua classe:

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

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

Questo potrebbe risparmiare un po 'di scrittura se si desidera rinominare Foo.


1
Modo divertente per farlo, ma non funzionerà con l'ereditarietà multipla.
Fisico pazzo,

5
E se hai più di 1 classe base? Ad ogni modo, è sciocco e spesso inutile provare a piegare il C ++ per assomigliare ad un altro linguaggio. Basta ricordare il nome della base e chiamarlo così.
underscore_d

6

Se vuoi chiamare una funzione della classe base dalla sua classe derivata puoi semplicemente chiamare all'interno della funzione sovrascritta citando il nome della classe base (come Foo :: printStuff () ).

il codice va qui

#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();
}

Ancora una volta puoi determinare in fase di esecuzione quale funzione chiamare usando l'oggetto di quella classe (derivata o base). Ma ciò richiede che la tua funzione nella classe base debba essere contrassegnata come virtuale.

codice sotto

#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

controllare questo...

#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;
}

produzione

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

il modo migliore è usare la funzione base :: come dire @sth


Come spiegato in questa domanda , questo non dovrebbe funzionare a causa protecteddell'eredità. Per lanciare un puntatore di classe base è necessario utilizzare l'eredità pubblica.
Darkproduct il

Interessante. Dopo aver letto questa risposta, ho pensato con eredità protetta che il fatto che Derivato derivasse dalla Base sarebbe visibile solo alla classe stessa e non visibile anche all'esterno.
Darkproduct il

0

Sì, puoi chiamarlo. La sintassi C ++ per chiamare la funzione della classe genitore nella classe figlio è

class child: public parent {
  // ...

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

Ulteriori informazioni sulla sostituzione delle funzioni .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.