Wie rufe ich eine übergeordnete Klassenfunktion aus einer abgeleiteten Klassenfunktion auf?

604

Wie rufe ich die übergeordnete Funktion aus einer abgeleiteten Klasse mit C ++ auf? Zum Beispiel habe ich eine Klasse namens parentund eine Klasse namens, childdie vom Elternteil abgeleitet ist. Innerhalb jeder Klasse gibt es eine printFunktion. Bei der Definition der Druckfunktion des Kindes möchte ich die Druckfunktion der Eltern anrufen. Wie würde ich das machen?

IaCoder
quelle
Bei allen oben genannten Lösungen wird davon ausgegangen, dass Ihre Druckfunktion eine statische Methode ist. Ist das der Fall? Wenn die Methode nicht statisch ist, sind die oben genannten Lösungen nicht relevant.
Hhafez
14
hhafez, Sie irren sich, dass die Syntax base :: function () wie eine statische Methodenaufrufsyntax aussieht, aber in diesem Kontext beispielsweise für Methoden funktioniert.
Motti
2
Ich würde den MSVC __super nicht verwenden, da er plattformspezifisch ist. Obwohl Ihr Code möglicherweise nicht auf einer anderen Plattform ausgeführt wird, würde ich die anderen Vorschläge verwenden, da sie dies als beabsichtigte Sprache tun.
Teaser
Das Antimuster, in dem abgeleitete Klassen immer erforderlich sind , um übergeordnete Klassenfunktionen aufzurufen, ist Call super
Rufus

Antworten:

775

Ich gehe das Risiko ein, das Offensichtliche zu sagen: Sie rufen die Funktion auf, wenn sie in der Basisklasse definiert ist, ist sie automatisch in der abgeleiteten Klasse verfügbar (sofern dies nicht der Fall ist private).

Wenn die abgeleitete Klasse eine Funktion mit derselben Signatur enthält, können Sie sie eindeutig unterscheiden, indem Sie den Namen der Basisklasse gefolgt von zwei Doppelpunkten hinzufügen base_class::foo(...). Sie sollten beachten, dass C ++ im Gegensatz zu Java und C # kein Schlüsselwort für "die Basisklasse" ( superoder base) hat, da C ++ Mehrfachvererbung unterstützt, was zu Mehrdeutigkeiten führen kann.

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

Im Übrigen können Sie nicht zweimal direkt von derselben Klasse ableiten, da es keine Möglichkeit gibt, auf eine der Basisklassen über die andere zu verweisen.

class bottom : public left, public left { // Illegal
};
Motti
quelle
31
Warum möchten Sie zweimal von derselben Klasse erben?
Paul Brewczynski
65
@bluesm: In der klassischen OOP macht es nicht viel Sinn, aber in der generischen Programmierung template<class A, class B> class C: public A, public B {};können zwei Typen aus Gründen, die davon abhängen, wie Ihr Code verwendet wird (das macht A und B gleich), zwei oder drei sein Abstraktionsschicht Weg von jemandem, der nicht weiß, was Sie getan haben.
Emilio Garavaglia
8
Ich denke, es ist nützlich hinzuzufügen, dass dies die übergeordnete Klassenmethode aufruft, selbst wenn sie nicht direkt in der übergeordneten Klasse implementiert ist , sondern in einer der übergeordneten Klassen in der Vererbungskette.
Maxim Lawrow
4
Nebenbei bemerkt, es hat mich wütend gemacht, als ich versuchte, dies in eine CPP-Datei zu schreiben. Ich hatte 'using namespace std'. 'left' wird irgendwo in diesem Namespace definiert. Das Beispiel würde nicht kompilieren - hat mich verrückt gemacht :). Dann habe ich 'left' in 'Left' geändert. Tolles Beispiel übrigens.
Mathai
72
@Mathai Und deshalb solltest du nicht verwenden using namespace std.
JAB
193

Wenn die übergeordnete Klasse Parentund die untergeordnete Klasse benannt sind Child, können Sie Folgendes tun:

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

Beachten Sie, dass dies Parentder tatsächliche Name der Klasse und kein Schlüsselwort ist.

Greg Hewgill
quelle
Dies wäre natürlich nur dann nützlich, wenn der Basisaufruf mit einer anderen Logik durchsetzt wäre, da es sonst keinen Sinn macht, die Funktion zu überschreiben. Vielleicht ist es ein wenig zu auf den Punkt gebracht;)
underscore_d
1
@underscore_d ist tatsächlich nützlich, selbst wenn der Basisaufruf nicht mit anderer Logik durchsetzt war. Nehmen wir an, die Elternklasse macht so ziemlich alles, was Sie wollen, macht aber eine Methode foo () verfügbar, die Benutzer von child nicht verwenden sollen - entweder weil foo () für child bedeutungslos ist oder externe Aufrufer von child das Kind vermasseln, was Kind ist tun. Daher kann das Kind in bestimmten Situationen parent :: foo () verwenden, jedoch eine Implementierung von foo bereitstellen, damit das foo () des Elternteils nicht aufgerufen wird.
Iheanyi
@iheanyi Klingt interessant, aber sorry, ich verstehe es noch nicht. Ist foo()hier analog print()oder eine separate Funktion? Und meinen Sie damit, dass Sie die privateVererbung verwenden, um von der Basis geerbte Details auszublenden und publicSchattenfunktionen für Dinge bereitzustellen , die Sie verfügbar machen möchten?
underscore_d
@underscore_d Ja, foo()war analog zu print(). Lassen Sie mich wieder auf die Verwendung zurückkommen, print()da ich denke, dass dies in diesem Zusammenhang sinnvoller wäre. Angenommen, jemand hat eine Klasse erstellt, die eine Reihe von Operationen für einen bestimmten Datentyp ausgeführt, einige Accessoren verfügbar gemacht und eine print(obj&)Methode verwendet hat. Ich brauche eine neue Klasse, die funktioniert, array-of-objaber alles andere ist gleich. Die Komposition führt zu viel dupliziertem Code. Die Vererbung minimiert dies beim print(array-of-obj&) Schleifenaufruf print(obj&), möchte aber nicht, dass Clients anrufen, print(obj&)da dies für sie keinen Sinn
ergibt
@underscore_d Dies basiert auf der Annahme, dass ich die gemeinsamen Teile der ursprünglichen Elternklasse nicht umgestalten kann oder dass dies unglaublich kostspielig ist. Private Vererbung könnte funktionieren, aber dann verlieren Sie die öffentlichen Zugriffsmethoden, auf die Sie sich verlassen haben - und müssten daher Code duplizieren.
Iheanyi
32

Wenn Ihre Basisklasse aufgerufen Basewird und Ihre Funktion aufgerufen wird FooBar(), können Sie sie direkt mit aufrufenBase::FooBar()

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

void ChildOfBase::FooBar()
{
  Base::FooBar();
}
Andrew Rollings
quelle
28

In MSVC gibt es dafür ein Microsoft-spezifisches Schlüsselwort: __super


MSDN: Hier können Sie explizit angeben, dass Sie eine Basisklassenimplementierung für eine Funktion aufrufen, die Sie überschreiben.

// 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
quelle
5
Eh, ich würde es vorziehen, typdefdie Eltern als so etwas zu betrachten super.
Thomas Eding
26
Ich werde nicht versuchen, die Verwendung von zu rechtfertigen __super; Ich habe es hier als alternativen Vorschlag erwähnt. Entwickler sollten ihren Compiler kennen und die Vor- und Nachteile seiner Funktionen verstehen.
Andrey
13
Ich möchte lieber niemanden davon abhalten, es zu verwenden, da es die Portabilität des Codes stark beeinträchtigt.
Erbureth sagt Reinstate Monica
26
Ich stimme Andrey nicht zu: Entwickler sollten den Standard kennen und sich nicht mit Compilerfunktionen beschäftigen müssen, wenn wir überlegen, Software zu schreiben, die in erster Linie vom Compiler unabhängig ist, was ich sowieso für eine gute Idee halte, da früher oder später in großen Projekten mehrere Compiler vorhanden sind werden sowieso verwendet.
Gabriel
7
"Entwickler sollten ihren Compiler kennen" Diese Argumentation und die Einbeziehung nicht standardmäßiger Funktionen führten zu IE6 ...
e2-e4
5

Wenn der Zugriffsmodifikator der Basisklassenmitgliedsfunktion geschützt ODER öffentlich ist, können Sie die Mitgliedsfunktion der Basisklasse aus der abgeleiteten Klasse aufrufen. Der Aufruf der nicht-virtuellen und virtuellen Elementfunktion der Basisklasse von der abgeleiteten Elementfunktion kann erfolgen. Bitte beziehen Sie sich auf das Programm.

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

Ausgabe:

$ 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
quelle
1
Vielen Dank, dass Sie einige Beispiele mitgebracht haben virtual!
M.Ioan
5

Rufen Sie die übergeordnete Methode mit dem übergeordneten Bereichsauflösungsoperator auf.

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

Referenzbeispiel.

superbem
quelle
2
Könnten Sie bitte eine Erklärung darüber bearbeiten, warum / wie dieser Code die Frage beantwortet? Von Nur-Code-Antworten wird abgeraten, da sie nicht so einfach zu lernen sind wie Code mit einer Erklärung. Ohne eine Erklärung nimmt es erheblich mehr Zeit und Mühe in Anspruch, zu verstehen, was getan wurde, welche Änderungen am Code vorgenommen wurden oder ob der Code nützlich ist. Die Erklärung ist sowohl für Personen wichtig, die versuchen, aus der Antwort zu lernen, als auch für Personen, die die Antwort bewerten, um festzustellen, ob sie gültig ist oder eine Abstimmung wert ist.
Makyen
3
Diese Antwort bezieht sich auf verschachtelte Klassen, während es sich bei der Frage um abgeleitete Klassen handelte (obwohl die Wörter "Eltern" und "Kind" etwas irreführend sind) und beantwortet die Frage daher überhaupt nicht.
Johannes Matokic