Czy mogę wywołać funkcję wirtualną klasy podstawowej, jeśli ją zastępuję?

340

Powiedzmy, że mam zajęcia Fooi Barskonfigurowałem tak:

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

Jak napisano w kodzie, chciałbym móc wywołać funkcję klasy bazowej, którą zastępuję. W Javie jest super.funcname()składnia. Czy jest to możliwe w C ++?

Alex
źródło
1
Dla Googlersów: zwróć uwagę, że możesz mieć problemy, tak jak ja, podczas przechowywania go jako zmiennej członka klasy, która nie jest wskaźnikiem. Zobacz moją odpowiedź tutaj: stackoverflow.com/questions/4798966/ ... Włączyłem nowy / usuń, aby naprawić.
Andrew

Odpowiedzi:

449

Składnia C ++ wygląda następująco:

class Bar : public Foo {
  // ...

  void printStuff() {
    Foo::printStuff(); // calls base class' function
  }
};
coś
źródło
11
Czy są z tym jakieś problemy? Czy to zła praktyka?
Robben_Ford_Fan_boy
36
@David: Nie, robienie tego jest całkowicie normalne, chociaż może to zależeć od twojej klasy, jeśli jest to faktycznie przydatne. Wywoływaj metodę klasy bazowej tylko wtedy, gdy coś się wydarzy;).
sth
20
Ostrożnie, to jest zapach kodu, gdy Twoi klienci są WYMAGANI! (nazywany call super requirement, sprawdź to tutaj: en.wikipedia.org/wiki/Call_super )
v.oddou
8
@ v.oddou: Nie ma nic złego w wywoływaniu superklasy, gdy jest to przydatne. Ta strona, do której prowadzą linki, dotyczy tylko pewnego potencjalnego problemu związanego z dziedziczeniem. Mówi nawet, że nie ma nic złego w wywoływaniu superklasy w ogólności: „Zauważ, że wymóg wywołania rodzica jest anty-wzorcem. Istnieje wiele przykładów w prawdziwym kodzie, w których metoda w podklasie może nadal chcesz funkcjonalność nadklasy, zwykle tam, gdzie tylko zwiększa funkcjonalność nadrzędną. ”
sth
26
Może to być oczywiste dla większości, ale dla kompletności pamiętaj, aby nigdy nie robić tego w konstruktorach i destruktorach.
TigerCoding
123

Tak,

class Bar : public Foo
{
    ...

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

Jest taki sam jak superw Javie, z tą różnicą, że pozwala na wywoływanie implementacji z różnych baz, gdy masz wiele elementów dziedziczenia.

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();
    }
};
Alex B.
źródło
7
To lepsza odpowiedź niż wybrana. Dzięki.
Mad Physicist
Wielokrotne dziedziczenie? Ojej!
lamino
69

Czasami trzeba wywołać implementację klasy podstawowej, gdy nie ma się funkcji pochodnej ... Nadal działa:

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;
}
AlwaysTraining
źródło
2
Zauważ, że musisz być zarówno xtypem, jak Base*i użyć Base::Fooskładni, aby to zadziałało
TTimo,
27

Na wypadek, gdybyś zrobił to dla wielu funkcji w swojej klasie:

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

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

To może zaoszczędzić trochę pisania, jeśli chcesz zmienić nazwę Foo.

MartinStettner
źródło
1
Świetny sposób, aby to zrobić, ale nie będzie działał z wielokrotnym dziedziczeniem.
Mad Physicist
5
A jeśli masz więcej niż 1 klasę podstawową? W każdym razie próba zgięcia C ++, aby wyglądała jak inny język, jest głupia i często daremna. Zapamiętaj tylko nazwę bazy i nazwij ją tak.
underscore_d
6

Jeśli chcesz wywołać funkcję klasy bazowej z jej klasy pochodnej, możesz po prostu wywołać funkcję nadpisaną z podaniem nazwy klasy bazowej (jak Foo :: printStuff () ).

kod idzie tutaj

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

Ponownie możesz określić w czasie wykonywania, którą funkcję wywołać, używając obiektu tej klasy (pochodnej lub podstawowej). Ale to wymaga, aby twoja funkcja w klasie podstawowej musiała być oznaczona jako wirtualna.

kod poniżej

#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();
}
Tunvir Rahman Tusher
źródło
0

Sprawdź to...

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

wynik

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

najlepszym sposobem jest użycie funkcji base :: jak powiedzmy @sth

joseAndresGomezTovar
źródło
Jak wyjaśniono w tym pytaniu , nie powinno to działać z powodu protecteddziedziczenia. Aby rzucić wskaźnik klasy bazowej, musisz użyć dziedziczenia publicznego.
Darkproduct
Ciekawy. Po przeczytaniu tej odpowiedzi pomyślałem z chronionym dziedzictwem, że fakt, że Pochodna pochodzi z Bazy, będzie widoczny tylko dla samej klasy, a także dla zewnątrz.
Darkproduct,
0

Tak, możesz to nazwać. Składnia C ++ do wywoływania funkcji klasy nadrzędnej w klasie potomnej to

class child: public parent {
  // ...

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

Przeczytaj więcej o zastępowaniu funkcji .

Atif
źródło