Jak wywołać funkcję klasy nadrzędnej z pochodnej funkcji klasy?

602

Jak wywołać funkcję nadrzędną z klasy pochodnej przy użyciu C ++? Na przykład mam klasę o nazwie parenti klasę o nazwie childpochodzącą od rodzica. W każdej klasie jest printfunkcja. W definicji funkcji drukowania dziecka chciałbym wywołać funkcję drukowania dla rodziców. Jak miałbym to zrobić?

IaCoder
źródło
Wszystkie powyższe rozwiązania zakładają, że funkcja drukowania jest metodą statyczną. Czy tak jest w przypadku? Jeśli metoda nie jest statyczna, powyższe rozwiązania nie są istotne.
hhafez
14
hhafez, mylisz się, składnia base :: function () wygląda jak statyczna składnia wywołań metod, ale w tym kontekście działa na przykład dla metod.
Motti,
2
Nie używałbym MSVC __super, ponieważ jest on specyficzny dla platformy. Chociaż Twój kod może nie działać na żadnej innej platformie, skorzystam z innych sugestii, ponieważ robią to zgodnie z zamierzonym językiem.
Teaser
Antypatternem, w którym klasy pochodne są zawsze wymagane do wywoływania funkcji klas nadrzędnych, jest Call super
Rufus

Odpowiedzi:

774

Zaryzykuję stwierdzenie oczywistości: wywołujesz funkcję, jeśli jest zdefiniowana w klasie bazowej, jest automatycznie dostępna w klasie pochodnej (chyba że jest private).

Jeśli w klasie pochodnej istnieje funkcja o tej samej sygnaturze, można ją ujednoznacznić, dodając nazwę klasy podstawowej, a następnie dwa dwukropki base_class::foo(...). Należy zauważyć, że w przeciwieństwie do Java i C #, C ++ nie ma słowa kluczowego dla „klasy podstawowej” ( superlub base), ponieważ C ++ obsługuje wielokrotne dziedziczenie, które może prowadzić do niejednoznaczności.

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

Nawiasem mówiąc, nie można dwa razy wywodzić bezpośrednio z tej samej klasy, ponieważ nie będzie możliwości odniesienia się do jednej z klas podstawowych nad drugą.

class bottom : public left, public left { // Illegal
};
Motti
źródło
30
Dlaczego chcesz dziedziczyć dwa razy po tej samej klasie?
Paul Brewczynski
65
@bluesm: w klasycznym OOP nie ma to większego sensu, ale w programowaniu ogólnym template<class A, class B> class C: public A, public B {};może dojść do dwóch typów, które są takie same z powodów zależnych od sposobu użycia kodu (co sprawia, że ​​A i B są takie same), może być dwóch lub trzech warstwa abstrakcji od kogoś, kto nie jest świadomy tego, co zrobiłeś.
Emilio Garavaglia,
8
Myślę, że warto dodać, że wywoła to metodę klasy nadrzędnej, nawet jeśli nie jest ona zaimplementowana bezpośrednio w klasie nadrzędnej, ale jest zaimplementowana w jednej z klas nadrzędnych w łańcuchu dziedziczenia.
Maxim Lavrov
4
Na marginesie, doprowadziło mnie to do szału, gdy próbowałem umieścić to w pliku CPP. Miałem „używanie przestrzeni nazw std”. „left” jest zdefiniowane gdzieś w tej przestrzeni nazw. Przykład nie dałby się skompilować - doprowadził mnie do szału :). Potem zmieniłem „lewy” na „lewy”. Nawiasem mówiąc, świetny przykład.
Mathai
72
@Mathai I dlatego nie powinieneś używać using namespace std.
JAB,
193

Biorąc pod uwagę nazwaną klasę nadrzędną Parenti nazwaną klasę podrzędną Child, możesz zrobić coś takiego:

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

Zauważ, że Parentjest to rzeczywista nazwa klasy, a nie słowo kluczowe.

Greg Hewgill
źródło
Oczywiście przydałoby się to tylko wtedy, gdy wywołanie podstawowe było przeplatane inną logiką, w przeciwnym razie nie byłoby sensu nadpisywanie funkcji, więc może to trochę za bardzo ;)
underscore_d
1
@underscore_d faktycznie jest przydatny, nawet jeśli wywołanie podstawowe nie było przeplatane inną logiką. Powiedzmy, że klasa nadrzędna właściwie robi wszystko, co chcesz, ale ujawnia metodę foo (), której nie chcesz, aby użytkownicy dziecka korzystali - albo dlatego, że foo () jest bez znaczenia w potomku, albo zewnętrzne wywołania do dziecka psują to, co dziecko robić. Dziecko może więc używać Parent :: foo () w niektórych sytuacjach, ale może zapewnić implementację foo, aby ukryć wywołanie funkcji foo () rodzica.
iheanyi 22.04.16
@ iheanyi Brzmi interesująco, ale przepraszam, jeszcze tego nie rozumiem. Czy jest foo()tu analogiczna print()funkcja czy osobna funkcja? Czy masz na myśli wykorzystanie privatedziedziczenia do ukrycia szczegółów odziedziczonych z bazy i zapewnienie publicfunkcji cieniowania rzeczy, które chcesz ujawnić?
underscore_d
@underscore_d Tak, foo()był analogiczny do print(). Wróćmy do używania, print()ponieważ moim zdaniem miałoby to większy sens w tym kontekście. Powiedzmy, że ktoś stworzył klasę, która przeprowadziła pewien zestaw operacji na określonym typie danych, ujawniła niektóre akcesoria i miała print(obj&)metodę. Potrzebuję nowej klasy, która działa, array-of-objale wszystko inne jest takie samo. Kompozycja powoduje powstanie dużej ilości powielonego kodu. Dziedziczenie minimalizuje to podczas print(array-of-obj&) wywoływania w pętli print(obj&), ale nie chce, aby klienci dzwonili, print(obj&)ponieważ nie ma dla nich sensu
iheanyi
@underscore_d Jest to oparte na założeniu, że nie mogę dokonać refaktoryzacji części wspólnych oryginalnej klasy nadrzędnej lub że jest to niezwykle kosztowne. Dziedziczenie prywatne może działać, ale wtedy tracisz dostęp publiczny, na którym polegałeś - a zatem musiałbyś duplikować kod.
iheanyi
32

Jeśli twoja klasa podstawowa jest wywoływana Base, a twoja funkcja jest wywoływana FooBar(), możesz wywołać ją bezpośrednio za pomocąBase::FooBar()

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

void ChildOfBase::FooBar()
{
  Base::FooBar();
}
Andrew Rollings
źródło
28

W MSVC istnieje słowo kluczowe specyficzne dla Microsoft: __super


MSDN: Umożliwia jawne stwierdzenie, że wywołujesz implementację klasy podstawowej dla zastępowanej funkcji.

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

Andrey
źródło
5
Eh, wolałbym typdefrodzica jak coś takiego super.
Thomas Eding,
26
Nie będę próbował uzasadniać użycia __super; Wspomniałem o tym tutaj jako alternatywnej sugestii. Programiści powinni znać swojego kompilatora oraz zrozumieć zalety i wady jego możliwości.
Andrey
13
Wolę zniechęcić kogokolwiek do korzystania z niego, ponieważ poważnie utrudnia to przenoszenie kodu.
Erbureth mówi: Przywróć Monikę
26
Nie zgadzam się z Andrey: programiści powinni znać standard i nie powinni zawracać sobie głowy funkcjami kompilatora, jeśli weźmiemy pod uwagę pisanie oprogramowania, które jest przede wszystkim niezależne od kompilatora, co uważam za dobry pomysł, ponieważ wcześniej czy później w dużych projektach wiele kompilatorów są w każdym razie używane.
Gabriel,
7
„Programiści powinni znać swojego kompilatora” to rozumowanie oraz włączenie niestandardowych funkcji doprowadziło do IE6 ...
e2-e4
5

Jeśli modyfikator dostępu do funkcji członka klasy bazowej jest chroniony LUB publiczny, możesz wykonać funkcję wywołania klasy bazowej z klasy pochodnej. Można wywoływać nie-wirtualne i wirtualne funkcje klasy podstawowej z pochodnej funkcji składowej. Proszę odnieść się do programu.

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

Wynik:

$ 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
Ajay Yadav
źródło
1
Dziękujemy za przyniesienie kilku przykładów virtual!
M.Ioan
5

Wywołaj metodę nadrzędną za pomocą operatora rozstrzygania zakresu nadrzędnego.

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
    }
};
Dean P.
źródło
-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();
}

Przykład odniesienia.

superbem
źródło
2
Czy możesz edytować wyjaśnienie, dlaczego / jak ten kod odpowiada na pytanie? Odpowiedzi zawierające tylko kod są odradzane, ponieważ nie są tak łatwe do nauczenia się jak kod z wyjaśnieniem. Bez wyjaśnienia potrzeba znacznie więcej czasu i wysiłku, aby zrozumieć, co zostało zrobione, zmiany wprowadzone w kodzie lub czy kod jest użyteczny. Wyjaśnienie jest ważne zarówno dla osób próbujących uczyć się na podstawie odpowiedzi, jak i tych, którzy oceniają odpowiedź, aby sprawdzić, czy jest ona ważna lub warta głosowania.
Makyen,
3
Ta odpowiedź dotyczy klas zagnieżdżonych, podczas gdy pytanie dotyczyło klas pochodnych (chociaż słowa „rodzic” i „dziecko” są nieco mylące), a zatem w ogóle nie odpowiada na pytanie.
Johannes Matokic,