C++ constructeur de copie implicite pour une classe qui contient d'autres objets

Je sais que le compilateur fournit parfois un constructeur de copie par défaut si vous ne vous implémentez pas. Je suis confus au sujet de ce que fait exactement ce constructeur. Si j'ai une classe qui contient d'autres objets, dont aucun n'a de constructeur de copie déclaré, quel sera le comportement? Par exemple, une classe comme celle-ci:

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

Maintenant, si je fais ceci:

Foo f1;
Foo f2(f1);

Que fera le constructeur de copie par défaut? Le constructeur de copie généré par le compilateur dans Foo appellera-t-il le constructeur généré par le compilateur dans Bar pour copier sur bar, qui appellera alors le constructeur de copie généré par le compilateur dans Baz?

44
demandé sur Ben Voigt 2009-11-27 21:41:17

5 réponses

Foo f1;
Foo f2(f1);

Oui, cela fera ce que vous attendez:
Le constructeur de copie F2 Foo:: Foo (Foo const&) est appelé.
Cette copie construit sa classe de base, puis chaque membre (récursivement)

Si vous définissez une classe comme ceci:

class X: public Y
{
    private:
        int     m_a;
        char*   m_b;
        Z       m_c;
};

Les méthodes suivantes seront définies par votre compilateur.

  • Constructeur (par défaut) (2 versions)
  • Constructeur (Copie)
  • Destructeur (par défaut)
  • affectation opérateur

Constructeur: Par Défaut:

Il y a en fait deux constructeurs par défaut.
L'un est utilisé pour zero-initialization, tandis que l'autre est utilisé pour value-initialization. L'utilisation dépend si vous utilisez () lors de l'initialisation ou non.

// Zero-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
    ,m_a(0)             // Default construction of basic PODS zeros them
    ,m_b(0)             // 
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
{
}

// Value-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
    //,m_a()            // Default construction of basic PODS does nothing
    //,m_b()            // The values are un-initialized.
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
{
}

Notes: si la classe de base ou les membres n'ont pas de constructeur par défaut visible valide, le constructeur par défaut ne peut pas être généré. Ce n'est pas une erreur sauf si votre code essaie d'utiliser le constructeur par défaut (alors seulement une compilation erreur d').

Constructeur (Copie)

X::X(X const& copy)
    :Y(copy)            // Calls the base copy constructor
    ,m_a(copy.m_a)      // Calls each members copy constructor
    ,m_b(copy.m_b)
    ,m_c(copy.m_c)
{}

Notes: si la classe de base ou les membres n'ont pas de constructeur de copie visible valide, le constructeur de copie ne peut pas être généré. Ce n'est pas une erreur sauf si votre code essaie d'utiliser le constructeur de copie (alors seulement une erreur de compilation).

Opérateur D'Affectation

X& operator=(X const& copy)
{
    Y::operator=(copy); // Calls the base assignment operator
    m_a = copy.m_a;     // Calls each members assignment operator
    m_b = copy.m_b;
    m_c = copy.m_c;

    return *this;
}

Notes: si la classe de base ou les membres n'ont pas d'opérateur d'affectation viable valide, l'opérateur d'affectation ne peut pas être généré. Ce n'est pas une erreur sauf si votre code essaie d'utiliser l'opérateur d'affectation (alors seulement une erreur de compilation).

Destructeur

X::~X()
{
                        // First runs the destructor code
}
    // This is psudo code.
    // But the equiv of this code happens in every destructor
    m_c.~Z();           // Calls the destructor for each member
    // m_b              // PODs and pointers destructors do nothing
    // m_a          
    ~Y();               // Call the base class destructor
  • Si un constructeur (y compris copy) est déclaré, le constructeur par défaut n'est pas implémenté par le compilateur.
  • Si le constructeur de copie est déclaré, le compilateur n'en générera pas.
  • Si l'opérateur d'affectation est déclaré, le compilateur n'en générera pas.
  • Si un destructeur est déclaré le compilateur ne générera pas un.

Regardant votre code les constructeurs de copie suivants sont générés:

Foo::Foo(Foo const& copy)
    :bar(copy.bar)
{}

Bar::Bar(Bar const& copy)
    :i(copy.i)
    ,baz(copy.baz)
{}

Baz::Baz(Baz const& copy)
    :j(copy.j)
{}
66
répondu Martin York 2012-11-12 17:48:10

Le compilateur fournit un constructeur de copie sauf si vousdéclarez (note: Pasdéfinissez ) vous-même. Le constructeur de copie généré par le compilateur appelle simplement le constructeur de copie de chaque membre de la classe (et de chaque classe de base).

La même chose est vraie pour L'opérateur d'affectation et le destructeur, BTW. C'est différent pour le constructeur par défaut, cependant: cela n'est fourni par le compilateur que si vous ne déclarez aucun autre constructeur vous-même.

11
répondu sbi 2009-11-27 18:45:08

Oui, le constructeur de copie généré par le compilateur effectue une copie par membre, dans l'ordre dans lequel les membres sont déclarés dans la classe contenant. Si l'un des types de membres n'offre pas lui-même de constructeur de copie, le constructeur de copie potentiel de la classe contenant ne peut pas être généré. Il peut toujours être possible d'en écrire un manuellement, si vous pouvez décider des moyens appropriés pour initialiser la valeur du membre qui ne peut pas être copié - construit-peut - être en utilisant l'un de ses autres constructeur.

2
répondu seh 2009-11-27 19:32:18

Le constructeur de copie par défaut C++ crée une copie peu profonde . Une copie superficielle ne créera pas de nouvelles copies d'objets que votre objet d'origine peut référencer; les anciens et les nouveaux objets contiendront simplement des pointeurs distincts vers le même emplacement de mémoire.

0
répondu Phil 2009-11-27 18:58:32

Le compilateur va générer les constructeurs nécessaires pour vous.

Cependant, dès que vous définissez vous-même un constructeur de copie, le compilateur renonce à générer quoi que ce soit pour cette classe et donnera et erreur si vous n'avez pas défini les constructeurs appropriés.

En utilisant votre exemple:

class Baz {
    Baz(const Baz& b) {}
    int j;
};
class Bar {
    int i;
    Baz baz;
};
class Foo {
    Bar bar;
};

Essayer d'instancier par défaut ou copy-construct Foo lancera une erreur puisque Baz n'est pas copy-constructible et que le compilateur ne peut pas générer le constroctor par défaut et copy pour les Foo.

0
répondu Coincoin 2009-12-07 20:37:05