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

21voto

CR. Punkte 96

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

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

Denken Sie auch daran, dass ein Konstruktor, der in der übergeordneten Klasse keine Parameter benötigt, automatisch aufgerufen wird, bevor der untergeordnete Konstruktor ausgeführt wird.

12voto

edW Punkte 1839

Wenn Sie Standardparameter in Ihrem Basiskonstruktor 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

0 Stimmen

Der Grund dafür ist, dass diese spezielle Erklärung eine implizite Base() die den gleichen Körper hat wie Base(int) sondern zusätzlich einen impliziten Initialisierer für : _a{1} . Es ist Base() der immer aufgerufen wird, wenn kein spezifischer Basiskonstruktor in der init-Liste verkettet ist. Und, wie bereits an anderer Stelle erwähnt, machen die delegierenden Konstruktoren von C++11 und die Klammer-oder-Gleich-Initialisierung Standardargumente eher weniger notwendig (während sie in vielen Beispielen bereits code-smell-esque waren).

10voto

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

7voto

Krishna Oza Punkte 1224

Niemand hat die Reihenfolge der Konstruktoraufrufe erwähnt, wenn eine Klasse von mehreren Klassen abgeleitet ist. Die Reihenfolge ist wie bei der Ableitung der Klassen angegeben.

2 Stimmen

Wenn niemand darüber gesprochen hat, wo wurde es dann erwähnt?

3 Stimmen

@EJP Da es in der Frage um Aufforderungsregeln geht, ist es sinnvoll, die Reihenfolge der Aufforderung in der Antwort zu erwähnen

4voto

Markus Dutschke Punkte 6917

Wenn Sie einfach nur alle Konstruktorargumente an die Basisklasse übergeben (=Eltern), hier ein minimales Beispiel.

Dabei werden Vorlagen verwendet, um jeden Konstruktoraufruf mit 1, 2 oder 3 Argumenten an die übergeordnete Klasse weiterzuleiten std::string .

Code

Live-Version

#include <iostream>
#include <string>

class ChildString: public std::string
{
    public:
        template<typename... Args>
        ChildString(Args... args): std::string(args...)
        {
            std::cout 
                << "\tConstructor call ChildString(nArgs="
                << sizeof...(Args) << "): " << *this
                << std::endl;
        }

};

int main()
{
    std::cout << "Check out:" << std::endl;
    std::cout << "\thttp://www.cplusplus.com/reference/string/string/string/" << std::endl;
    std::cout << "for available string constructors" << std::endl;

    std::cout << std::endl;
    std::cout << "Initialization:" << std::endl;
    ChildString cs1 ("copy (2)");

    char char_arr[] = "from c-string (4)";
    ChildString cs2 (char_arr);

    std::string str = "substring (3)";
    ChildString cs3 (str, 0, str.length());

    std::cout << std::endl;
    std::cout << "Usage:" << std::endl;
    std::cout << "\tcs1: " << cs1 << std::endl;
    std::cout << "\tcs2: " << cs2 << std::endl;
    std::cout << "\tcs3: " << cs3 << std::endl;

    return 0;
}

Ausgabe

Check out:
    http://www.cplusplus.com/reference/string/string/string/
for available string constructors

Initialization:
    Constructor call ChildString(nArgs=1): copy (2)
    Constructor call ChildString(nArgs=1): from c-string (4)
    Constructor call ChildString(nArgs=3): substring (3)

Usage:
    cs1: copy (2)
    cs2: from c-string (4)
    cs3: substring (3)

Update: Variadic-Vorlagen verwenden

Zur Verallgemeinerung auf n Argumente und zur Vereinfachung

        template <class C>
        ChildString(C arg): std::string(arg)
        {
            std::cout << "\tConstructor call ChildString(C arg): " << *this << std::endl;
        }
        template <class C1, class C2>
        ChildString(C1 arg1, C2 arg2): std::string(arg1, arg2)
        {
            std::cout << "\tConstructor call ChildString(C1 arg1, C2 arg2, C3 arg3): " << *this << std::endl;
        }
        template <class C1, class C2, class C3>
        ChildString(C1 arg1, C2 arg2, C3 arg3): std::string(arg1, arg2, arg3)
        {
            std::cout << "\tConstructor call ChildString(C1 arg1, C2 arg2, C3 arg3): " << *this << std::endl;
        }

zu

template<typename... Args>
        ChildString(Args... args): std::string(args...)
        {
            std::cout 
                << "\tConstructor call ChildString(nArgs="
                << sizeof...(Args) << "): " << *this
                << std::endl;
        }

3 Stimmen

Ich nehme leichten Anstoß daran, dass ein so schönes Beispiel die Verwendung von std::endl überall. Die Leute sehen das und bauen es in Schleifen ein und wundern sich, warum das Schreiben eines Haufens von Zeilen in eine Textdatei "in C++" 5x-20x langsamer ist als mit fprintf . TL;DR: Verwenden "\n" (zu einem vorhandenen String-Literal hinzugefügt, falls vorhanden), und verwenden Sie std::endl nur, wenn Sie die Puffer in die Datei leeren müssen (z.B. zum Debuggen, wenn der Code abstürzt und Sie seine letzten Worte sehen wollen). Ich denke, dass std::endl war ein Designfehler der Bequemlichkeit: ein cooles "Gadget", das viel mehr kann, als der Name vermuten lässt.

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