840 Stimmen

Was sind die Regeln für den Aufruf des Konstruktors der Basisklasse?

Wie lauten die C++-Regeln für den Aufruf des Basisklassenkonstruktors aus einer abgeleiteten Klasse?

Ich weiß zum Beispiel, dass man das in Java in der ersten Zeile des Unterklassenkonstruktors tun muss (und wenn man das nicht tut, wird ein impliziter Aufruf eines Super-Konstruktors ohne Argumente angenommen, was zu einem Kompilierfehler führt, wenn das fehlt).

21 Stimmen

Nur eine Erbsenzählerei: In C++ gibt es keine "Superklasse", in der Norm wird sie überhaupt nicht erwähnt. Diese Formulierung stammt (höchstwahrscheinlich) aus Java. In C++ wird "Basisklasse" verwendet. Ich denke, dass super impliziert einen einzigen Elternteil, während C++ Mehrfachvererbung zulässt.

0 Stimmen

@andreee Ich redete, dass ein super class wird auch als base class und auch z.B. im qt toolkit parent class - in dieser Reihenfolge a sub class wird auch als child class Vielleicht hilft das, eine mögliche Verwirrung in der Terminologie zu bekämpfen

1133voto

luke Punkte 34409

Konstruktoren von Basisklassen werden automatisch für Sie aufgerufen, wenn sie kein Argument haben. Wenn Sie einen Oberklassenkonstruktor mit einem Argument aufrufen wollen, müssen Sie die Konstruktorinitialisierungsliste der Unterklasse verwenden. Im Gegensatz zu Java unterstützt C++ die Mehrfachvererbung (im Guten wie im Schlechten), so dass auf die Basisklasse mit dem Namen und nicht mit "super()" verwiesen werden muss.

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 über die Initialisierungsliste des Konstruktors aquí y aquí .

57 Stimmen

Ich habe "explicit" aus dem SuperClass-Konstruktor entfernt. Obwohl dies eine bewährte Praxis für Konstruktoren mit einem Argument ist, war es für die vorliegende Diskussion nicht von Belang. Weitere Informationen über das Schlüsselwort explicit finden Sie unter: weblogs.asp.net/kennykerr/archive/2004/08/31/

1 Stimmen

Der Doppelpunkt : Operator, den Sie verwendet haben, um den Konstruktor der Oberklasse aufzurufen, bevor Sie den Konstruktor der Unterklasse instanziieren, ich nehme an, das gilt auch für Methoden?

3 Stimmen

@hagubear, gilt nur für Konstruktoren, AFAIK

281voto

puetzk Punkte 10094

In C++ werden die Konstruktoren ohne Argumente für alle Superklassen und Mitgliedsvariablen für Sie aufgerufen, bevor Sie Ihren Konstruktor eingeben. Wenn Sie ihnen Argumente übergeben wollen, gibt es dafür eine eigene Syntax, die "Konstruktorverkettung" genannt wird und wie folgt aussieht:

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

Wenn irgendetwas, das an diesem Punkt ausgeführt wird, fehlschlägt, 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 wollen, müssen Sie einen Try-Block für die Funktion 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;
};

In dieser Form ist zu beachten, dass der try-Block ist Dies ermöglicht es ihm, Ausnahmen abzufangen, die durch implizite oder explizite Initialisierungen von Mitgliedern und Basisklassen sowie während des Funktionskörpers ausgelöst werden. Wenn ein Funktions-Catch-Block jedoch keine andere Ausnahme auslöst, wird die Laufzeit den ursprünglichen Fehler erneut auslösen; Ausnahmen während der Initialisierung kann nicht ignoriert werden.

1 Stimmen

Ich bin mir nicht sicher, ob ich die Syntax Ihres zweiten Beispiels verstehe... Ist das try/catch-Konstrukt ein Ersatz für den Konstruktorkörper?

2 Stimmen

Ja, ich habe den Abschnitt umformuliert und einen Fehler korrigiert (das Schlüsselwort try steht vor der Initialisierungsliste). Ich hätte es nachschlagen sollen, anstatt aus dem Gedächtnis zu schreiben, es ist nicht etwas, das oft verwendet wird :-)

127 Stimmen

Vielen Dank für die Einbeziehung der try/catch-Syntax für die Initialisierer. Ich benutze C++ seit 10 Jahren, und das ist das erste Mal, dass ich das gesehen habe.

62voto

Dima Punkte 37984

In C++ gibt es das Konzept der Initialisierungsliste des Konstruktors, in der Sie den Konstruktor der Basisklasse aufrufen können und sollten und in der Sie auch die Datenelemente initialisieren sollten. Die Initialisierungsliste steht nach der Konstruktorsignatur, gefolgt von einem Doppelpunkt, und vor dem Körper 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_;
};

Unter der Annahme, dass B einen Konstruktor hat, der einen int annimmt, kann der Konstruktor von A wie folgt aussehen:

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

Wie Sie sehen können, wird der Konstruktor der Basisklasse in der Initialisierungsliste aufgerufen. Die Initialisierung der Datenelemente in der Initialisierungsliste ist übrigens besser als die Zuweisung der Werte für b_ und c_ innerhalb des Konstruktorkörpers, da Sie die zusätzlichen Kosten der Zuweisung sparen.

Beachten Sie, dass Datenelemente immer in der Reihenfolge initialisiert werden, in der sie in der Klassendefinition deklariert sind, unabhängig von ihrer Reihenfolge in der Initialisierungsliste. Um seltsame Fehler zu vermeiden, die entstehen können, wenn Ihre Datenmitglieder voneinander abhängen, sollten Sie immer sicherstellen, dass die Reihenfolge der Mitglieder in der Initialisierungsliste und in der Klassendefinition gleich ist. Aus demselben Grund muss der Konstruktor der Basisklasse der erste Punkt in der Initialisierungsliste sein. Wenn Sie ihn ganz weglassen, wird automatisch der Standardkonstruktor der Basisklasse aufgerufen. Wenn die Basisklasse in diesem Fall keinen Standardkonstruktor hat, erhalten Sie einen Compilerfehler.

1 Stimmen

Warten Sie eine Sekunde... Sie sagen, Initialisierer sparen die Kosten für Zuweisungen. Aber finden in ihnen nicht die gleichen Zuweisungen statt, wenn sie aufgerufen werden?

6 Stimmen

Nö. Init und Zuweisung sind zwei verschiedene Dinge. Wenn ein Konstruktor aufgerufen wird, versucht er, jedes Datenelement mit dem zu initialisieren, was er für den Standardwert hält. In der Init-Liste können Sie Standardwerte angeben. Sie verursachen also in beiden Fällen Initialisierungskosten.

1 Stimmen

Und wenn Sie die Zuweisung innerhalb des Körpers verwenden, fallen ohnehin die Initialisierungskosten an, und dann kommen noch die Kosten für die Zuweisung hinzu.

32voto

TT_ Punkte 1599

Alle erwähnten einen Konstruktoraufruf durch eine Initialisierungsliste, aber niemand sagte, dass der Konstruktor einer Elternklasse explizit vom Konstruktorkörper des abgeleiteten Mitglieds aufgerufen werden kann. Siehe die Frage Aufrufen eines Konstruktors der Basisklasse aus dem Konstruktorkörper einer Unterklasse zum Beispiel. Der Punkt ist, dass ein expliziter Aufruf eines Konstruktors der Elternklasse oder der Superklasse im Rumpf einer abgeleiteten Klasse eigentlich nur eine Instanz der Elternklasse erzeugt und nicht den Konstruktor der Elternklasse für das abgeleitete Objekt aufruft. Die einzige Möglichkeit, den Konstruktor der Elternklasse oder der Superklasse für ein Objekt einer abgeleiteten Klasse aufzurufen, ist über die Initialisierungsliste und nicht im Körper des Konstruktors der abgeleiteten Klasse. Daher sollte es vielleicht nicht als "Superclass Constructor Call" bezeichnet werden. Ich habe diese Antwort hierher gestellt, weil jemand verwirrt sein könnte (so wie ich).

15 Stimmen

Diese Antwort ist etwas verwirrend, obwohl ich sie ein paar Mal durchgelesen und mir die verlinkte Frage angesehen habe. Ich glaube, sie besagt, dass ein expliziter Aufruf eines Konstruktors der Elternklasse oder der Superklasse im Rumpf einer abgeleiteten Klasse eigentlich nur eine Instanz der Elternklasse erzeugt und nicht den Konstruktor der Elternklasse für das abgeleitete Objekt aufruft. Die einzige Möglichkeit, den Konstruktor einer übergeordneten Klasse oder einer Superklasse für ein Objekt einer abgeleiteten Klasse aufzurufen, besteht in der Initialisierungsliste und nicht im Körper des Konstruktors einer abgeleiteten Klasse.

1 Stimmen

@Richard Chambers Es mag verwirrend sein, da Englisch nicht meine Muttersprache ist, aber Sie haben genau beschrieben, was ich zu sagen versuchte.

0 Stimmen

"Der Konstruktor einer übergeordneten Klasse kann explizit aus dem Konstruktorkörper des abgeleiteten Mitglieds aufgerufen werden", ist für die fragliche Instanz offensichtlich falsch, es sei denn, Sie beziehen sich auf die Platzierung von new, und selbst dann ist es falsch, weil Sie die Instanz zuerst zerstören müssten. Z.B.. MyClass::MyClass() { new (this) BaseClass; /* UB, totally wrong */ } - Dies ist die C++-Syntax für expliziter Aufruf eines Konstruktors . So sieht ein "Konstruktoraufruf" aus. Die Tatsache, dass diese absurd falsche Antwort hochgestuft wurde, ist mir daher ein völliges Rätsel.

22voto

Nils Pipenbrinck Punkte 80152

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

Wenn Sie einen Basis-Konstruktor mit Argumenten aufrufen wollen, müssen Sie das explizit in den abgeleiteten Konstruktor schreiben, etwa so:

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

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

In C++ kann man eine abgeleitete Klasse nicht konstruieren, ohne den Konstruktor der Elternklasse aufzurufen. Das geschieht entweder automatisch, wenn es sich um einen Nicht-Arg-C'tor handelt, es geschieht, wenn Sie den abgeleiteten Konstruktor direkt aufrufen, wie oben gezeigt, oder Ihr Code lässt sich nicht kompilieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X