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



Answers

Nein, Sie können keinen Konstruktor in C ++ 03 (als delegierender Konstruktor bezeichnet) von einem anderen Konstruktor aus aufrufen.

Dies änderte sich in C ++ 11 (aka C ++ 0x), die Unterstützung für die folgende Syntax hinzugefügt:
(Beispiel aus Wikipedia )

class SomeType
{
  int number;

public:
  SomeType(int newNumber) : number(newNumber) {}
  SomeType() : SomeType(42) {}
};
Question

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.




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.




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.




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.




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.




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.




Wenn du böse sein willst, kannst du den direkten "neuen" Operator verwenden:

class Foo() {
    Foo() { /* default constructor deliciousness */ }
    Foo(Bar myParam) {
      new (this) Foo();
      /* bar your param all night long */
    } 
};

Scheint für mich zu arbeiten.

bearbeiten

Wie @ElvedinHamzagic hervorhebt, wenn Foo ein Objekt enthält, das Speicher zugewiesen hat, wird dieses Objekt möglicherweise nicht freigegeben. Dies verkompliziert die Dinge weiter.

Ein allgemeines Beispiel:

class Foo() {
private:
  std::vector<int> Stuff;
public:
    Foo()
      : Stuff(42)
    {
      /* default constructor deliciousness */
    }

    Foo(Bar myParam)
    {
      this->~Foo();
      new (this) Foo();
      /* bar your param all night long */
    } 
};

Sieht etwas weniger elegant aus. @ John Idol's Lösung ist viel besser.




Links