it-swarm.com.de

Wie lauten die Regeln für den Aufruf des Superklassenkonstruktors?

Wie lauten die C++ - Regeln zum Aufrufen des Superklassenkonstruktors aus einer Unterklasse?

Ich weiß zum Beispiel, dass Sie dies in Java als erste Zeile des Unterklassenkonstruktors tun müssen (und wenn Sie dies nicht tun, wird ein impliziter Aufruf eines No-Arg-Superkonstruktors vorausgesetzt - was zu einem Kompilierungsfehler führt, wenn dieser fehlt). .

639
levik

Basisklassenkonstruktoren werden automatisch für Sie aufgerufen, wenn sie kein Argument haben. Wenn Sie einen Superklassenkonstruktor mit einem Argument aufrufen möchten, müssen Sie die Konstruktorinitialisierungsliste der Unterklasse verwenden. Im Gegensatz zu Java unterstützt C++ Mehrfachvererbung (besser oder schlechter), daher muss auf die Basisklasse mit dem Namen und nicht mit "super ()" verwiesen werden.

class SuperClass
{
    public:

        SuperClass(int foo)
        {
            // do something with foo
        }
};

class SubClass : public SuperClass
{
    public:

        SubClass(int foo, int bar)
        : SuperClass(foo)    // Call the superclass constructor in the subclass' initialization list.
        {
            // do something with bar
        }
};

Weitere Informationen zur Initialisierungsliste des Konstruktors hier und hier .

865
luke

In C++ werden die Konstruktoren ohne Argumente für alle Superklassen und Membervariablen für Sie aufgerufen, bevor Sie Ihren Konstruktor eingeben. Wenn Sie ihnen Argumente übergeben möchten, gibt es eine separate Syntax namens "Konstruktorkettung", die folgendermaßen aussieht:

class Sub : public Base
{
  Sub(int x, int y)
  : Base(x), member(y)
  {
  }
  Type member;
};

Wenn zu diesem Zeitpunkt etwas ausgeführt wird, werden die Destruktoren der Basen/Mitglieder, die zuvor die Konstruktion abgeschlossen hatten, aufgerufen, und die Ausnahme wird an den Aufrufer zurückgegeben. Wenn Sie während der Verkettung Ausnahmen abfangen möchten, müssen Sie einen Funktionsversuchsblock verwenden:

class Sub : public Base
{
  Sub(int x, int y)
  try : Base(x), member(y)
  {
    // function body goes here
  } catch(const ExceptionType &e) {
    throw kaboom();
  }
  Type member;
};

Beachten Sie in dieser Form, dass der try-Block is der Hauptteil der Funktion ist, anstatt sich im Hauptteil der Funktion zu befinden. Auf diese Weise können Ausnahmen abgefangen werden, die durch implizite oder explizite Member- und Basisklasseninitialisierungen sowie während des Hauptteils der Funktion ausgelöst werden. Wenn ein Funktions-Catch-Block jedoch keine andere Ausnahme auslöst, wird der ursprüngliche Fehler von der Laufzeit erneut ausgelöst. Ausnahmen während der Initialisierung können nicht ignoriert werden.

223
puetzk

In C++ gibt es ein Konzept für die Initialisierungsliste des Konstruktors, in dem Sie den Konstruktor der Basisklasse aufrufen können und sollten und in dem Sie auch die Datenelemente initialisieren sollten. Die Initialisierungsliste steht nach der Konstruktorsignatur nach einem Doppelpunkt und vor dem Hauptteil des Konstruktors. Nehmen wir an, wir haben eine Klasse A:


class A : public B
{
public:
  A(int a, int b, int c);
private:
  int b_, c_;
};

Angenommen, B hat einen Konstruktor, der ein int annimmt, dann könnte der Konstruktor von A so aussehen:


A::A(int a, int b, int c) 
  : B(a), b_(b), c_(c) // initialization list
{
  // do something
}

Wie Sie sehen, wird der Konstruktor der Basisklasse in der Initialisierungsliste aufgerufen. Das Initialisieren der Datenelemente in der Initialisierungsliste ist übrigens der Zuweisung der Werte für b_ und c_ im Rumpf des Konstruktors vorzuziehen, da Sie die zusätzlichen Zuweisungskosten sparen.

Beachten Sie, dass Datenelemente unabhängig von ihrer Reihenfolge in der Initialisierungsliste immer in der Reihenfolge initialisiert werden, in der sie in der Klassendefinition deklariert sind. Um seltsame Fehler zu vermeiden, die auftreten können, wenn Ihre Datenmitglieder voneinander abhängig sind, sollten Sie immer sicherstellen, dass die Reihenfolge der Mitglieder in der Initialisierungsliste und der Klassendefinition identisch ist. Aus dem gleichen Grund muss der Basisklassenkonstruktor das erste Element in der Initialisierungsliste sein. Wenn Sie es ganz weglassen, wird der Standardkonstruktor für die Basisklasse automatisch aufgerufen. In diesem Fall erhalten Sie einen Compilerfehler, wenn die Basisklasse keinen Standardkonstruktor hat.

50
Dima

Jeder erwähnte einen Konstruktoraufruf über eine Initialisierungsliste, aber niemand sagte, dass der Konstruktor einer übergeordneten Klasse explizit aus dem Konstruktorkörper des abgeleiteten Mitglieds aufgerufen werden kann. Siehe beispielsweise die Frage Aufruf eines Konstruktors der Basisklasse aus dem Konstruktorkörper einer Unterklasse . Der Punkt ist, dass, wenn Sie einen expliziten Aufruf einer übergeordneten Klasse oder eines übergeordneten Klassenkonstruktors im Hauptteil einer abgeleiteten Klasse verwenden, dies tatsächlich nur eine Instanz der übergeordneten Klasse erstellt und nicht den übergeordneten Klassenkonstruktor für das abgeleitete Objekt aufruft . Die einzige Möglichkeit, einen übergeordneten Klassen- oder Superklassenkonstruktor für ein Objekt einer abgeleiteten Klasse aufzurufen, besteht in der Initialisierungsliste und nicht im Rumpf des abgeleiteten Klassenkonstruktors. Vielleicht sollte es nicht als "Konstruktoraufruf der Oberklasse" bezeichnet werden. Ich habe diese Antwort hierher gestellt, weil jemand verwirrt werden könnte (wie ich).

21
TT_

Wenn Sie einen Konstruktor ohne Argumente haben, wird dieser aufgerufen, bevor der abgeleitete Klassenkonstruktor ausgeführt wird.

Wenn Sie einen Basiskonstruktor mit Argumenten aufrufen möchten, müssen Sie explizit Folgendes in den abgeleiteten Konstruktor schreiben:

class base
{
  public:
  base (int arg)
  {
  }
};

class derived : public base
{
  public:
  derived () : base (number)
  {
  }
};

Sie können keine abgeleitete Klasse erstellen, ohne den übergeordneten Konstruktor in C++ aufzurufen. Dies geschieht entweder automatisch, wenn es sich um ein C'tor ohne Argument handelt, oder, wenn Sie den abgeleiteten Konstruktor wie oben gezeigt direkt aufrufen, oder Ihr Code wird nicht kompiliert.

19

Die einzige Möglichkeit, Werte an einen übergeordneten Konstruktor zu übergeben, besteht in einer Initialisierungsliste. Die Initialisierungsliste wird mit einem: und dann einer Liste von Klassen und den Werten implementiert, die an diesen Klassenkonstruktor übergeben werden sollen.

Class2::Class2(string id) : Class1(id) {
....
}

Denken Sie auch daran, dass ein Konstruktor, der keine Parameter für die übergeordnete Klasse akzeptiert, automatisch aufgerufen wird, bevor der untergeordnete Konstruktor ausgeführt wird.

19
CR.

Wenn Sie in Ihrem Basiskonstruktor Standardparameter haben, wird die Basisklasse automatisch aufgerufen.

using namespace std;

class Base
{
    public:
    Base(int a=1) : _a(a) {}

    protected:
    int _a;
};

class Derived : public Base
{
  public:
  Derived() {}

  void printit() { cout << _a << endl; }
};

int main()
{
   Derived d;
   d.printit();
   return 0;
}

Ausgang ist: 1

11
edW
CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }
9
Dynite

Niemand erwähnte die Reihenfolge der Konstruktoraufrufe, wenn eine Klasse aus mehreren Klassen stammt. Die Reihenfolge ist wie beim Ableiten der Klassen angegeben.

7
krishna_oza