2009-04-16 39 views
14

So che se si lascia un membro da un elenco di inizializzazione in un costruttore no-arg, verrà chiamato il costruttore predefinito di quel membro.Elenchi di inizializzazione del costruttore di copia

I costruttori di copia chiamano allo stesso modo il costruttore di copia dei membri o chiamano anche il costruttore predefinito?

class myClass { 
    private: 
    someClass a; 
    someOtherClass b; 
    public: 
    myClass() : a(DEFAULT_A) {} //implied is b() 
    myClass(const myClass& mc) : a(mc.a) {} //implied is b(mc.b)??? or is it b()? 
} 
+2

Vedere: http://stackoverflow.com/questions/563221/is-there-an-implicit-default-constructor-in-c/563320#563320 –

risposta

23

I costruttori di copia esplicitamente definiti non chiamano i costruttori di copia per i membri.

Quando si inserisce il corpo di un costruttore, tutti i membri di quella classe verranno inizializzati. Cioè, una volta arrivato a { hai la certezza che tutti i tuoi membri sono stati inizializzati.

Se non specificato, i membri vengono inizializzati automaticamente nell'ordine in cui appaiono nella classe. (E se non possono essere, il programma è mal formato). Quindi, se si definisce il proprio costruttore di copie, ora spetta a voi chiamare tutti i costruttori di copia dei membri come desiderato.

Ecco un piccolo programma è possibile copiare e incollare da qualche parte e pasticciare con:

#include <iostream> 

class Foo { 
public: 
    Foo() { 
     std::cout << "In Foo::Foo()" << std::endl; 
    } 

    Foo(const Foo& rhs) { 
     std::cout << "In Foo::Foo(const Foo&)" << std::endl; 
    } 
}; 

class Bar { 
public: 
    Bar() { 
     std::cout << "In Bar::Bar()" << std::endl; 
    } 

    Bar(const Bar& rhs) { 
     std::cout << "In Bar::Bar(const Bar&)" << std::endl; 
    } 
}; 

class Baz { 
public: 
    Foo foo; 
    Bar bar; 

    Baz() { 
     std::cout << "In Baz::Baz()" << std::endl; 
    } 

    Baz(const Baz& rhs) { 
     std::cout << "In Baz::Baz(const Baz&)" << std::endl; 
    } 
}; 

int main() { 
    Baz baz1; 
    std::cout << "Copying..." << std::endl; 
    Baz baz2(baz1); 
} 

così com'è, questa stampa:

 
In Foo::Foo() 
In Bar::Bar() 
In Baz::Baz() 
Copying... 
In Foo::Foo() 
In Bar::Bar() 
In Baz::Baz(const Baz&) 

Nota che è default-inizializzare i membri del Baz.

commentando il costruttore di copia esplicita, come:

/* 
Baz(const Baz& rhs) { 
    std::cout << "In Baz::Baz(const Baz&)" << std::endl; 
} 
*/ 

L'uscita sarà questo:

 
In Foo::Foo() 
In Bar::Bar() 
In Baz::Baz() 
Copying... 
In Foo::Foo(const Foo&) 
In Bar::Bar(const Bar&) 

chiama il costruttore di copia su entrambi.

E se reintrodurre Baz 's costruttore di copia e copiare in modo esplicito un singolo membro:

Baz(const Baz& rhs) : 
    foo(rhs.foo) 
{ 
    std::cout << "In Baz::Baz(const Baz&)" << std::endl; 
} 

Otteniamo:

 
In Foo::Foo() 
In Bar::Bar() 
In Baz::Baz() 
Copying... 
In Foo::Foo(const Foo&) 
In Bar::Bar() 
In Baz::Baz(const Baz&) 

Come si può vedere, una volta che si dichiara in modo esplicito un costruttore di copia è responsabili della copia di tutti i membri della classe; è il tuo costruttore ora.

Questo vale per tutti i costruttori, inclusi i costruttori di spostamenti.

+0

E se un membro è un puntatore raw (ad esempio void *) o int, double, ecc. - il costruttore di copia definito dall'utente assegnerà 0 a prima di entrare {se l'utente non assegna nulla a questi membri nella lista di inizializzazione del costruttore di copie? –

+0

@SergeRogatch: se non lo si inizializza in modo esplicito, il valore non è specificato come una normale variabile non inizializzata e la sua lettura è un comportamento non definito. È necessario inizializzare esplicitamente i puntatori a null, interi a 0, ecc. – GManNickG

2

Sì. I medici sono dei medici.

+0

+1, mi hai battuto di 2 secondi. :) –

+1

-1: Cosa significa "Sì" come risposta a una domanda contenente un "o"? – mmmmmmmm

+1

rstevens, la domanda è stata modificata poco dopo la risposta di Charlie. Charlie ha risposto alla domanda originale perfettamente. Detto questo, ho modificato la mia risposta qui sotto e penso sia abbastanza buono :) – GManNickG

2

Per qualsiasi variabile membro con un costruttore predefinito viene richiamato il costruttore predefinito se non è stata aggiunta esplicitamente alcuna chiamata del costruttore per quella variabile membro nell'elenco di inizializzazione.

0

Quando il compilatore fornisce il cctor predefinito, cosa pensa che faccia il compilatore per le variabili membro? È copia costruisce esso.

Allo stesso modo, se il cctor è definito dall'utente e se uno lascia fuori alcuni membri, questi membri non possono essere lasciati non inizializzati. Gli invarianti di classe sono stabiliti durante la costruzione e devono essere costantemente mantenuti. Quindi, il compilatore lo fa per te.

+0

-1 scusa. Un copy ctor predefinito fornito dal compilatore * copierà ogni membro usando il proprio copistocaster (che è una copia bit a bit nel caso di tipi primitivi). –

+0

Sì, ma sto dicendo la stessa cosa! * Default Inizializza * indica la copia tramite il membro cctor. – Abhay

+0

Vedo quello che stai dicendo, ma in realtà il termine "inizializzazioni di default" ha un significato specifico e ben definito nello standard C++, che è quello di inizializzare un oggetto con il valore predefinito del tipo (beh, è ​​leggermente più complicato ma comunque ...) Quindi la tua descrizione è un po 'fuorviante. –

1

Non c'è nulla di magico in un costruttore di copie, a parte il fatto che il compilatore lo aggiungerà se necessario. Ma nel modo in cui viene eseguito, non c'è niente di speciale - se non dici esplicitamente "usa un tale costruttore", userà l'impostazione predefinita.

1

Non in VC9. Non sono sicuro degli altri.

// compiled as: cl /EHsc contest.cpp 
// 
// Output was: 
// Child1() 
// ----- 
// Child1() 
// Child2() 
// Parent() 
// ----- 
// Child1(Child1&) 
// Child2() 
// Parent(Parent&) 

#include <cstdio> 

class Child1 { 
    int x; 
public: 
    static Child1 DEFAULT; 

    Child1(){ 
     x = 0; 
     printf("Child1()\n"); 
    } 

    Child1(Child1 &other){ 
     x = other.x; 
     printf("Child1(Child1&)\n"); 
    } 
}; 

Child1 Child1::DEFAULT; 

class Child2 { 
    int x; 
public: 
    Child2(){ 
     x = 0; 
     printf("Child2()\n"); 
    } 

    Child2(Child2 &other){ 
     x = other.x; 
     printf("Child2(Child2&)\n"); 
    } 
}; 

class Parent { 
    int x; 
    Child1 c1; 
    Child2 c2; 

public: 
    Parent(){ 
     printf("Parent()\n"); 
    } 

    Parent(Parent &other) : c1(Child1::DEFAULT) { 
     printf("Parent(Parent&)\n"); 
    } 
}; 

int main(){ 
    printf("-----\n"); 
    Parent p1; 
    printf("-----\n"); 
    Parent p2(p1); 

    return 0; 
} 
+0

E lo stdout era? –

2

Per maggiori dettagli si veda: Is there an implicit default constructor in C++?

breve:

  • compilatore ha generato "Default Costruttore": utilizza il costruttore di default di ogni membro.
  • Compiler generato "Copia costruttore": utilizza il costruttore di copie di ciascun membro.
  • Compilatore "Operatore di assegnazione" generato: utilizza l'operatore di assegnazione di ciascun membro.
1

A seconda di come viene avviato il costruttore di chiamata di base, i costruttori del membro verranno chiamati allo stesso modo. Per esempio, cominciamo con:

struct ABC{ 
    int a; 
    ABC() : a(0) { printf("Default Constructor Called %d\n", a); }; 

    ABC(ABC & other) 
    { 
     a=other.a; 
     printf("Copy constructor Called %d \n" , a) ; 
    }; 
}; 

struct ABCDaddy{ 
    ABC abcchild; 
}; 

si possono fare queste prove:

printf("\n\nTest two, where ABC is a member of another structure\n"); 
ABCDaddy aD; 
aD.abcchild.a=2; 

printf("\n Test: ABCDaddy bD=aD; \n"); 
ABCDaddy bD=aD; // Does call the copy constructor of the members of the structure ABCDaddy (ie. the copy constructor of ABC is called) 

printf("\n Test: ABCDaddy cD(aD); \n"); 
ABCDaddy cD(aD); // Does call the copy constructor of the members of the structure ABCDaddy (ie. the copy constructor of ABC is called) 

printf("\n Test: ABCDaddy eD; eD=aD; \n"); 
ABCDaddy eD; 
eD=aD;   // Does NOT call the copy constructor of the members of the structure ABCDaddy (ie. the copy constructor of ABC is not called) 

uscita:

Default Constructor Called 0 

Test: ABCDaddy bD=aD; 
Copy constructor Called 2 

Test: ABCDaddy cD(aD); 
Copy constructor Called 2 

Test: ABCDaddy eD; eD=aD; 
Default Constructor Called 0 

godere.

Problemi correlati