constructor constructor - Posso chiamare un costruttore da un altro costruttore(fare il concatenamento del costruttore)in C++?




with parameters (13)

Se comprendo correttamente la tua domanda, stai chiedendo se puoi chiamare più costruttori in C ++?

Se è quello che stai cercando, allora no - non è possibile.

Puoi certamente avere più costruttori, ognuno con firme di argomento univoche, e poi chiamare quello che vuoi quando installi un nuovo oggetto.

Puoi persino avere un costruttore con argomenti predefiniti alla fine.

Ma potresti non avere più costruttori e quindi chiamarli separatamente.

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.


Questo approccio può funzionare per alcuni tipi di classi (quando l'operatore di assegnazione si comporta "bene"):

Foo::Foo()
{
    // do what every Foo is needing
    ...
}

Foo::Foo(char x)
{
    *this = Foo();

    // do the special things for a Foo with char
    ...
}

C ++ 11: Sì!

C ++ 11 e successivi ha questa stessa caratteristica (chiamata delegazione dei costruttori ).

La sintassi è leggermente diversa da C #:

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

C ++ 03: no

Sfortunatamente, non c'è modo di farlo in C ++ 03, ma ci sono due modi per simularlo:

  1. È possibile combinare due (o più) costruttori tramite parametri predefiniti:

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
  2. Utilizzare un metodo init per condividere codice comune:

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

Vedere la voce delle domande frequenti su C ++ come riferimento.


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.


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

Credo che puoi chiamare un costruttore da un costruttore. Si compilerà e correrà. Di recente ho visto qualcuno fare questo e funzionava sia su Windows che su Linux.

Semplicemente non fa quello che vuoi. Il costruttore interno costruirà un oggetto locale temporaneo che viene eliminato una volta che il costruttore esterno ritorna. Dovrebbero essere anche diversi costruttori o si dovrebbe creare una chiamata ricorsiva.

Rif: https://isocpp.org/wiki/faq/ctors#init-methods


Proporrei l'uso di un metodo private friend che implementa la logica dell'applicazione del costruttore ed è chiamato dai vari costruttori. Ecco un esempio:

Supponiamo di avere una classe chiamata StreamArrayReader con alcuni campi privati:

private:
    istream * in;
      // More private fields

E vogliamo definire i due costruttori:

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

Dove il secondo si serve semplicemente del primo (e ovviamente non vogliamo duplicare l'implementazione del primo). Idealmente, si vorrebbe fare qualcosa come:

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

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

Tuttavia, questo non è permesso in C ++. Per questo motivo, possiamo definire un metodo amico privato come segue che implementa ciò che il primo costruttore dovrebbe fare:

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

Ora questo metodo (perché è un amico) ha accesso ai campi privati ​​di o . Quindi, il primo costruttore diventa:

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

Si noti che questo non crea più copie per le copie appena create. Il secondo diventa:

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

Cioè, invece di avere un costruttore che chiama un altro, entrambi chiamano un amico privato!


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.


Sarebbe più facile testare, piuttosto che decidere :) Prova questo:

#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;
}

e compilarlo con 98 std: g ++ main.cpp -std = c ++ 98 -o test_1

vedrai:

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

così :)


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.


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.


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 essere praticabile.


Solo per curiosità ho dato un'occhiata a quello che succede sotto il cofano, e ho usato dtruss/strace su ogni test.

C ++

./a.out < in
Saw 6512403 lines in 8 seconds.  Crunch speed: 814050

syscalls sudo dtruss -c ./a.out < in

CALL                                        COUNT
__mac_syscall                                   1
<snip>
open                                            6
pread                                           8
mprotect                                       17
mmap                                           22
stat64                                         30
read_nocancel                               25958

Pitone

./a.py < in
Read 6512402 lines in 1 seconds. LPS: 6512402

syscalls sudo dtruss -c ./a.py < in

CALL                                        COUNT
__mac_syscall                                   1
<snip>
open                                            5
pread                                           8
mprotect                                       17
mmap                                           21
stat64                                         29






c++ constructor