[c++] Posso chiamare un costruttore da un altro costruttore (fare il concatenamento del costruttore) in C ++?



6 Answers

No, non è possibile chiamare un costruttore da un altro in C ++ 03 (chiamato costruttore delegante).

Questo è cambiato in C ++ 11 (aka C ++ 0x), che ha aggiunto il supporto per la seguente sintassi:
(esempio tratto da Wikipedia )

class SomeType
{
  int number;

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

Come sviluppatore C# sono abituato a correre attraverso i costruttori:

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

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

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

C'è un modo per farlo in C ++?

Ho provato a chiamare il nome della classe e ad usare la parola chiave "this", ma entrambi falliscono.




Se vuoi essere cattivo, puoi utilizzare l'operatore "nuovo" sul posto:

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

Sembra funzionare per me.

modificare

Come sottolinea @ElvedinHamzagic, se Foo conteneva un oggetto che assegnava memoria, quell'oggetto non poteva essere liberato. Questo complica ulteriormente le cose.

Un esempio più generale:

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 */
    } 
};

Sembra un po 'meno elegante, di sicuro. @ La soluzione di JohnIdol è molto meglio.




In Visual C ++ è inoltre possibile utilizzare questa notazione all'interno del costruttore: this-> Classname :: Classname (parametri di un altro costruttore). Guarda un esempio qui sotto:

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

Non so se funziona da qualche altra parte, l'ho testato solo in Visual C ++ 2003 e 2008. Potresti anche chiamare diversi costruttori in questo modo, suppongo, proprio come in Java e C #.

PS: Francamente, sono rimasto sorpreso dal fatto che questo non fosse menzionato prima.




Vale la pena sottolineare che puoi chiamare il costruttore di una classe genitore nel tuo costruttore, ad esempio:

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

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

Ma no, non puoi chiamare un altro costruttore della stessa classe.




Quando si chiama un costruttore, esso alloca effettivamente la memoria, dallo stack o dall'heap. Quindi chiamare un costruttore in un altro costruttore crea una copia locale. Quindi stiamo modificando un altro oggetto, non quello su cui ci stiamo concentrando.




In poche parole, non puoi prima di C ++ 11.

C ++ 11 introduce i costruttori deleganti :

Costruttore delegato

Se il nome della classe stessa appare come class-or-identificatore nell'elenco di inizializzazione dei membri, allora l'elenco deve essere costituito solo da quel solo inizializzatore del membro; tale costruttore è noto come costruttore delegante e il costruttore selezionato dal solo membro dell'elenco inizializzatore è il costruttore di destinazione

In questo caso, il costruttore di destinazione viene selezionato dalla risoluzione di sovraccarico ed eseguito per primo, quindi il controllo ritorna al costruttore delegante e il suo corpo viene eseguito.

I costruttori delegati non possono essere ricorsivi.

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

Si noti che un costruttore delegante è una proposta "tutto o niente"; se un costruttore delega a un altro costruttore, il costruttore chiamante non può avere altri membri nel suo elenco di inizializzazione. Questo ha senso se si pensa di inizializzare i membri const / reference una volta, e solo una volta.




Un'altra opzione che non è stata ancora mostrata è quella di dividere la tua classe in due, avvolgendo una classe di interfaccia leggera attorno alla tua classe originale per ottenere l'effetto che stai cercando:

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

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

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

Questo potrebbe diventare complicato se hai molti costruttori che devono chiamare la loro controparte "next level up", ma per una manciata di costruttori, dovrebbe funzionare.




Related