Kann ich die virtuelle Funktion einer Basisklasse aufrufen, wenn ich sie überschreibe?


340

Angenommen, ich habe Unterricht Foound bin so Bareingerichtet:

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

Wie im Code kommentiert, möchte ich die Funktion der Basisklasse aufrufen können, die ich überschreibe. In Java gibt es die super.funcname()Syntax. Ist das in C ++ möglich?



1
Für Googler: Beachten Sie, dass Sie möglicherweise Probleme haben, wie ich es beim Speichern als Klassenmitgliedsvariable getan habe, die kein Zeiger ist. Siehe meine Antwort hier: stackoverflow.com/questions/4798966/… Ich habe new / delete zum Fixieren verwendet.
Andrew

Antworten:


448

Die C ++ - Syntax lautet wie folgt:

class Bar : public Foo {
  // ...

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

11
Gibt es mögliche Probleme dabei? Ist es schlechte Praxis?
Robben_Ford_Fan_boy

36
@ David: Nein, es ist völlig normal, dies zu tun, obwohl es von Ihrer tatsächlichen Klasse abhängen kann, ob es tatsächlich nützlich ist. Rufen Sie die Basisklassenmethode nur auf, wenn Sie etwas tun möchten;).
etw

20
Vorsicht, dies ist ein Code-Geruch, wenn Ihre Kunden dazu aufgefordert werden! (genannt call super requirement,
schau es dir

8
@ v.oddou: Es ist nichts Falsches daran, die Superklasse aufzurufen, wenn es nützlich ist. Diese Seite, die Sie verlinkt haben, handelt nur von einem bestimmten potenziellen Problem im Zusammenhang mit der Vererbung. Es sagt sogar selbst, dass es nichts Falsches ist, die Superklasse im Allgemeinen aufzurufen: "Beachten Sie, dass es die Anforderung ist , das übergeordnete Element aufzurufen, das das Anti-Pattern ist. Es gibt viele Beispiele im realen Code, bei denen die Methode in der Unterklasse möglicherweise noch vorhanden ist möchte die Funktionalität der Oberklasse, normalerweise dort, wo sie nur die übergeordnete Funktionalität erweitert. "
etw

26
Für die meisten mag es offensichtlich sein, aber der Vollständigkeit halber denken Sie daran, dies niemals bei Konstruktoren und Destruktoren zu tun.
TigerCoding

123

Ja,

class Bar : public Foo
{
    ...

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

Es ist dasselbe wie superin Java, außer dass es das Aufrufen von Implementierungen von verschiedenen Basen aus ermöglicht, wenn Sie mehrere Vererbungen haben.

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
Dies ist eine bessere Antwort als die ausgewählte. Vielen Dank.
Mad Physicist

Mehrfachvererbung? Ooh yikes!
Lamino

69

Manchmal müssen Sie die Implementierung der Basisklasse aufrufen, wenn Sie nicht in der abgeleiteten Funktion sind ... Es funktioniert immer noch:

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
Beachten Sie, dass Sie beide xvom Typ sein Base*und die Base::FooSyntax verwenden müssen, damit dies funktioniert
TTimo

27

Nur für den Fall, dass Sie dies für viele Funktionen in Ihrer Klasse tun:

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

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

Dies kann ein wenig Schreiben sparen, wenn Sie Foo umbenennen möchten.


1
Unterhaltsame Möglichkeit, dies zu tun, funktioniert jedoch nicht mit Mehrfachvererbung.
Verrückter Physiker

5
Und was ist, wenn Sie mehr als eine Basisklasse haben? Wie auch immer, es ist albern und oft sinnlos zu versuchen, C ++ so zu biegen, dass es wie eine andere Sprache aussieht. Erinnern Sie sich einfach an den Namen der Basis und nennen Sie ihn so.
underscore_d

6

Wenn Sie eine Funktion der Basisklasse aus ihrer abgeleiteten Klasse aufrufen möchten, können Sie einfach die überschriebene Funktion mit dem Namen der Basisklasse aufrufen ( wie Foo :: printStuff () ).

Code geht hier

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

Wieder können Sie zur Laufzeit mithilfe des Objekts dieser Klasse (abgeleitet oder Basis) bestimmen, welche Funktion aufgerufen werden soll. Dies erfordert jedoch, dass Ihre Funktion in der Basisklasse als virtuell markiert ist.

Code unten

#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

Überprüfen Sie dies...

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

Ausgabe

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

Der beste Weg ist, die base :: -Funktion als say @sth zu verwenden


Wie in dieser Frage erläutert , sollte dies aufgrund der protectedVererbung nicht funktionieren . Um einen Basisklassenzeiger umzuwandeln, müssen Sie die öffentliche Vererbung verwenden.
Darkproduct


Interessant. Nachdem ich diese Antwort gelesen hatte , dachte ich mit geschützter Vererbung, dass die Tatsache, dass Derived von Base abgeleitet ist, nur für die Klasse selbst und nicht auch für die Außenseite sichtbar ist.
Darkproduct

0

Ja, du kannst es nennen. Die C ++ - Syntax zum Aufrufen der übergeordneten Klassenfunktion in der untergeordneten Klasse lautet

class child: public parent {
  // ...

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

Lesen Sie mehr über das Überschreiben von Funktionen .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.