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


Als ein C # Entwickler Ich bin es 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.


756
2017-11-21 09:43


Ursprung


Antworten:


C ++ 11: Ja!

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

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)
    {
      // ...
    }
    

Sehen der C ++ FAQ-Eintrag als Referenz.


1020
2017-11-21 10:04



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) {}
};

101
2017-11-21 10:00



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


38
2017-08-12 15:31



Es ist erwähnenswert, dass Sie kann Rufen Sie den Konstruktor einer Elternklasse in Ihrem Konstruktor auf.

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

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

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


19
2017-11-22 06:36



Im C ++ 11, ein Konstruktor kann eine andere Konstruktorüberladung aufrufen:

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.


17
2017-09-22 20:07



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.


11
2018-03-24 16:53



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.


7
2017-11-21 09:56



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 anrufen mehrere Konstruktoren auf diese Weise, denke ich, genau wie in Java und C #.

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


5
2018-02-20 10:00



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 die gewünschte Struktur 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.


2
2017-11-21 09:49



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.


2
2017-11-25 00:54



Ich würde den Gebrauch von a vorschlagen private friend Methode, 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 ist, Anstatt dass ein Konstruktor einen anderen anruft, rufen beide einen privaten Freund an!


2
2017-11-02 00:19