2009-04-16 5 views
14

Je sais que si vous laissez un membre hors d'une liste d'initialisation dans un constructeur sans argument, le constructeur par défaut de ce membre sera appelé.Copier les listes d'initialisation du constructeur

Les constructeurs de copie appellent-ils également le constructeur de copie des membres ou appellent-ils également le constructeur par défaut?

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

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

Répondre

23

Les constructeurs de copie définis explicitement n'appellent pas de constructeurs de copie pour les membres.

Lorsque vous entrez le corps d'un constructeur, chaque membre de cette classe sera initialisé. C'est-à-dire, une fois que vous obtenez à { vous êtes garanti que tous vos membres ont été initialisés.

Sauf indication contraire, les membres sont initialisés par défaut dans l'ordre dans lequel ils apparaissent dans la classe. (Et s'ils ne peuvent pas l'être, le programme est mal formé.) Donc, si vous définissez votre propre constructeur de copie, il vous appartient maintenant d'appeler n'importe quel constructeur de copie de membre comme vous le souhaitez.

Voici un petit programme que vous pouvez coller copier quelque part et mess avec:

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

As-est, cette impression:

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

Notez qu'il est réinitialisant par défaut les membres de Baz.

En commentant le constructeur de copie explicite, comme:

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

La sortie deviendra ceci:

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

Il appelle le constructeur de copie sur les deux.

Et si nous réintroduisons Baz EXEMPLAIRE constructeur et copier explicitement un seul membre:

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

Nous obtenons:

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

Comme vous pouvez le voir, une fois que vous déclarez explicitement un constructeur de copie vous sont responsables de la copie de tous les membres du groupe; c'est votre constructeur maintenant.

Cela s'applique à tous les constructeurs, y compris les constructeurs de déplacement.

+0

Que faire si un membre est un pointeur brut (par exemple void *) ou int, double, etc. - le constructeur de copie défini par l'utilisateur affectera 0 à les avant d'entrer {si l'utilisateur n'attribue rien à ces membres dans la liste d'initialisation du constructeur de copie? –

+0

@SergeRogatch: Si vous ne l'initialisez pas explicitement, la valeur n'est pas spécifiée comme une variable normale non initialisée et la lecture est un comportement indéfini. Vous devez initialiser explicitement les pointeurs sur null, ints sur 0, etc. – GManNickG

2

Oui. Les ctors sont des ctors.

+0

+1, vous me battez de 2 secondes. :) –

+1

-1: Que signifie "Oui" comme réponse à une question contenant un "ou"? – mmmmmmmm

+1

rstevens, la question a été éditée peu de temps après que Charlie ait répondu. Charlie a parfaitement répondu à la question initiale. Cela dit, j'ai édité ma réponse ci-dessous et je pense que c'est assez bon :) – GManNickG

2

Pour toute variable membre ayant un constructeur par défaut que le constructeur par défaut est appelé si vous n'avez pas explicitement ajouté tout autre appel de constructeur pour cette variable membre dans la liste d'initialisation.

0

Lorsque le compilateur fournit le cctor par défaut, que pensez-vous que le compilateur fait pour les variables membres? Il copie construit il.Dans la même veine, si le cctor est défini par l'utilisateur, et si l'on exclut certains membres, ces membres ne peuvent pas être laissés non initialisés. Les invariants de classe sont établis pendant la construction et doivent être constamment entretenus. Ainsi, le compilateur fait cela pour vous.

+0

-1 Désolé. Une copie par défaut fournie par le compilateur ctor * copiera chaque membre en utilisant la copie ctor de ce membre (qui est une copie bit à bit dans le cas de types primitifs). –

+0

Oui, mais je dis la même chose! * Initilalise * signifie copier par le membre cctor. – Abhay

+0

Je vois ce que vous dites, mais en fait le terme "initialise par défaut" a une signification spécifique et bien définie dans le standard C++, qui est d'initialiser un objet avec la valeur par défaut du type (c'est un peu plus compliqué mais de toute façon ...) Donc votre description est un peu trompeuse. –

1

Il n'y a rien de magique dans un constructeur de copie, si ce n'est que le compilateur l'ajoutera si nécessaire. Mais dans la façon dont il fonctionne réellement, il n'y a rien de spécial - si vous ne dites pas explicitement "utiliser tel ou tel constructeur", il utilisera la valeur par défaut.

1

Pas dans VC9. Pas sûr des autres.

// 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

Et le stdout était? –

2

Pour plus de détails voir: Is there an implicit default constructor in C++?

court:

  • compilateur généré "par défaut Constructeur": utilise le constructeur par défaut de chaque membre.
  • "Constructeur de copie" généré par le compilateur: utilise le constructeur de copie de chaque membre.
  • "Opérateur d'affectation" généré par le compilateur: utilise l'opérateur d'affectation de chaque membre.
1

Selon la façon dont le constructeur d'appel de base est initié, les constructeurs du membre seront appelés de la même manière. Par exemple, nous allons commencer par:

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

Vous pouvez faire ces tests:

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) 

Sortie:

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 

Profitez.

Questions connexes