from Kann ich einen Konstruktor von einem anderen Konstruktor(do constructor chaining) in C++ aufrufen?




java constructor (12)

Als C# -Entwickler bin ich gewohnt, Konstruktoren zu durchlaufen:

class Test {
    public Test() {
        DoSomething();
    }

    public Test(int count) : this() {
        DoSomethingWithCount(count);
    }

    public Test(int count, string name) : this(count) {
        DoSomethingWithName(name);
    }
}

Gibt es eine Möglichkeit, dies in C ++ zu tun?

Ich habe versucht, den Klassennamen aufzurufen und das Schlüsselwort 'this' zu verwenden, aber beides schlägt fehl.


Wenn Sie Ihre Frage richtig verstehen, fragen Sie, ob Sie mehrere Konstruktoren in C ++ aufrufen können.

Wenn Sie danach suchen, dann ist das nicht möglich.

Sie können sicherlich mehrere Konstruktoren haben, jeder mit eindeutigen Argumentsignaturen, und dann den gewünschten Namen bei der Instanziierung eines neuen Objekts aufrufen.

Sie können am Ende sogar einen Konstruktor mit default-Argumenten haben.

Aber Sie haben möglicherweise nicht mehrere Konstruktoren und rufen sie dann einzeln auf.


In C++11 kann ein Wikipedia :

class Foo  {
     int d;         
public:
    Foo  (int i) : d(i) {}
    Foo  () : Foo(42) {} //New to C++11
};

Darüber hinaus können Mitglieder auch so initialisiert werden.

class Foo  {
     int d = 5;         
public:
    Foo  (int i) : d(i) {}
};

Dadurch sollte die Initialisierungs-Hilfsmethode nicht mehr erstellt werden. Es wird weiterhin empfohlen, keine virtuellen Funktionen in den Konstruktoren oder Destruktoren aufzurufen, um die Verwendung von Membern zu vermeiden, die möglicherweise nicht initialisiert werden.


C ++ 11: Ja!

C ++ 11 und höher hat dieselbe Funktion ( delegierende Konstruktoren genannt ).

Die Syntax unterscheidet sich geringfügig von C #:

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {}
};

C ++ 03: Nein

Leider gibt es keine Möglichkeit, dies in C ++ 03 zu tun, aber es gibt zwei Möglichkeiten, dies zu simulieren:

  1. Sie können zwei (oder mehr) Konstruktoren über Standardparameter kombinieren:

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
  2. Verwenden Sie eine Init-Methode, um gemeinsamen Code zu teilen:

    class Foo {
    public:
      Foo(char x);
      Foo(char x, int y);
      // ...
    private:
      void init(char x, int y);
    };
    
    Foo::Foo(char x)
    {
      init(x, int(x) + 7);
      // ...
    }
    
    Foo::Foo(char x, int y)
    {
      init(x, y);
      // ...
    }
    
    void Foo::init(char x, int y)
    {
      // ...
    }
    

Siehe den C ++ - FAQ-Eintrag als Referenz.


In Visual C ++ können Sie diese Notation auch im Konstruktor verwenden: this-> Classname :: Classname (Parameter eines anderen Konstruktors). Siehe ein Beispiel unten:

class Vertex
{
 private:
  int x, y;
 public:
  Vertex(int xCoo, int yCoo): x(xCoo), y(yCoo) {}
  Vertex()
  {
   this->Vertex::Vertex(-1, -1);
  }
};

Ich weiß nicht, ob es woanders funktioniert, ich habe es nur in Visual C ++ 2003 und 2008 getestet. Sie können auch mehrere Konstruktoren auf diese Weise aufrufen, wie in Java und C #.

PS: Ehrlich gesagt war ich überrascht, dass dies nicht früher erwähnt wurde.


Beim Aufrufen eines Konstruktors weist er tatsächlich Speicher entweder vom Stapel oder vom Heap zu. Wenn Sie also einen Konstruktor in einem anderen Konstruktor aufrufen, wird eine lokale Kopie erstellt. Also modifizieren wir ein anderes Objekt, nicht das, auf das wir uns konzentrieren.


Es ist erwähnenswert, dass Sie den Konstruktor einer Elternklasse in Ihrem Konstruktor aufrufen können , zB:

class A { /* ... */ };

class B : public A
{
    B() : A()
    {
        // ...
    }
};

Aber, nein, Sie können keinen anderen Konstruktor derselben Klasse aufrufen.


Nein, in C ++ kann man keinen Konstruktor von einem Konstruktor aufrufen. Was Sie tun können, wie Warren sagte, ist:

  • Überladen Sie den Konstruktor mit verschiedenen Signaturen
  • Verwenden Sie Standardwerte für Argumente, um eine "einfachere" Version verfügbar zu machen

Beachten Sie, dass Sie im ersten Fall die Code-Duplizierung nicht reduzieren können, indem Sie einen Konstruktor von einem anderen Konstruktor aufrufen. Sie können natürlich eine separate private / protected-Methode verwenden, die die gesamte Initialisierung durchführt, und den Konstruktor hauptsächlich mit der Behandlung von Argumenten behandeln lassen.


Einfach gesagt, können Sie nicht vor C ++ 11.

C ++ 11 führt delegierende Konstruktoren ein :

Delegieren des Konstruktors

Wenn der Name der Klasse selbst als Klasse oder Kennung in der Memberinitialisierungsliste angezeigt wird, muss die Liste nur aus diesem einen Memberinitialisierer bestehen. Ein solcher Konstruktor wird als delegierender Konstruktor bezeichnet, und der Konstruktor, der vom einzigen Mitglied der Initialisierungsliste ausgewählt wird, ist der Zielkonstruktor

In diesem Fall wird der Zielkonstruktor durch Überladungsauflösung ausgewählt und zuerst ausgeführt, dann kehrt das Steuerelement zum delegierenden Konstruktor zurück und sein Rumpf wird ausgeführt.

Delegierende Konstruktoren können nicht rekursiv sein.

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)
};

Beachten Sie, dass ein delegierender Konstruktor ein Alles-oder-Nichts-Vorschlag ist; Wenn ein Konstruktor an einen anderen Konstruktor delegiert, darf der aufrufende Konstruktor keine anderen Mitglieder in seiner Initialisierungsliste haben. Dies ist sinnvoll, wenn Sie darüber nachdenken, const / reference-Mitglieder einmal und nur einmal zu initialisieren.


Ich würde vorschlagen, eine private friend Methode zu verwenden, die die Anwendungslogik des Konstruktors implementiert und von den verschiedenen Konstruktoren aufgerufen wird. Hier ist ein Beispiel:

Angenommen, wir haben eine Klasse namens StreamArrayReader mit einigen privaten Feldern:

private:
    istream * in;
      // More private fields

Und wir wollen die zwei Konstruktoren definieren:

public:
    StreamArrayReader(istream * in_stream);
    StreamArrayReader(char * filepath);
    // More constructors...

Wo der zweite einfach den ersten benutzt (und natürlich wollen wir die Umsetzung des ersteren nicht kopieren). Idealerweise würde man gerne so etwas machen wie:

StreamArrayReader::StreamArrayReader(istream * in_stream){
    // Implementation
}

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    StreamArrayReader(&instream);
    instream.close();
}

Dies ist jedoch in C ++ nicht erlaubt. Aus diesem Grund können wir eine private Friend-Methode wie folgt definieren, die implementiert, was der erste Konstruktor tun soll:

private:
  friend void init_stream_array_reader(StreamArrayReader *o, istream * is);

Nun hat diese Methode (weil sie ein Freund ist) Zugriff auf die privaten Felder von o . Dann wird der erste Konstruktor:

StreamArrayReader::StreamArrayReader(istream * is) {
    init_stream_array_reader(this, is);
}

Beachten Sie, dass dadurch nicht mehrere Kopien für die neu erstellten Kopien erstellt werden. Der zweite wird:

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    init_stream_array_reader(this, &instream);
    instream.close();
}

Das heißt, anstatt dass ein Konstruktor einen anderen anruft, rufen beide einen privaten Freund an!


Eine andere Option, die bisher noch nicht gezeigt wurde, ist die Aufteilung Ihrer Klasse in zwei, indem Sie eine leichte Interface-Klasse um Ihre ursprüngliche Klasse wickeln, um den gewünschten Effekt zu erzielen:

class Test_Base {
    public Test_Base() {
        DoSomething();
    }
};

class Test : public Test_Base {
    public Test() : Test_Base() {
    }

    public Test(int count) : Test_Base() {
        DoSomethingWithCount(count);
    }
};

Dies könnte unordentlich werden, wenn Sie viele Konstruktoren haben, die ihr "next level up" Gegenstück nennen müssen, aber für eine Handvoll Konstruktoren sollte es praktikabel sein.


Wäre einfacher zu testen, als zu entscheiden :) Versuchen Sie Folgendes:

#include <iostream>

class A {
public:
    A( int a) : m_a(a) {
        std::cout << "A::Ctor" << std::endl;    
    }
    ~A() {
        std::cout << "A::dtor" << std::endl;    
    }
public:
    int m_a;
};

class B : public A {
public:
    B( int a, int b) : m_b(b), A(a) {}
public:
    int m_b;
};

int main() {
    B b(9, 6);
    std::cout << "Test constructor delegation a = " << b.m_a << "; b = " << b.m_b << std::endl;    
    return 0;
}

und kompiliere es mit 98 std: g ++ main.cpp -std = c ++ 98 -o test_1

du wirst sehen:

A::Ctor
Test constructor delegation a = 9; b = 6
A::dtor

damit :)


Ich glaube, Sie können einen Konstruktor von einem Konstruktor aufrufen. Es wird kompiliert und ausgeführt. Ich habe kürzlich gesehen, dass jemand das macht und es lief sowohl unter Windows als auch unter Linux.

Es macht einfach nicht was du willst. Der innere Konstruktor erstellt ein temporäres lokales Objekt, das gelöscht wird, sobald der äußere Konstruktor zurückgegeben wird. Sie müssten auch andere Konstruktoren sein oder Sie würden einen rekursiven Aufruf erstellen.

Ref: https://isocpp.org/wiki/faq/ctors#init-methoden





constructor