Question

Je sais que le compilateur fournit parfois un constructeur de copie par défaut si vous ne l'implémentez pas vous-même. Je suis confus sur ce que fait exactement ce constructeur. Si ma classe 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 appelle-t-il le constructeur généré par le compilateur dans Bar pour copier sur bar , qui appellera ensuite le compilateur- constructeur de copie généré dans Baz ?

Était-ce utile?

La solution

Foo f1;
Foo f2(f1);

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

Si vous définissez une classe comme celle-ci:

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)
  • Destructor (par défaut)
  • Opérateur d'affectation

Constructeur: Par défaut:

Il existe actuellement deux constructeurs par défaut.
L'un est utilisé pour zéro-initialisation , tandis que l'autre est utilisé pour initialisation-valeur . L'utilisation dépend de si vous utilisez ou non () lors de l'initialisation.

// 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'
{
}

Remarques: Si la classe de base ou l'un de ses membres ne possède pas de constructeur par défaut visible visible, le constructeur par défaut ne peut pas être généré. Ce n'est pas une erreur, sauf si votre code tente d'utiliser le constructeur par défaut (il ne s'agit alors que d'une erreur de compilation).

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

Remarques: Si la classe de base ou l'un de ses membres ne possède pas de constructeur de copie visible valide, le constructeur de copie ne peut pas être généré. Ce n'est pas une erreur, à moins que votre code ne tente d'utiliser le constructeur de copie (il ne s'agit alors que d'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;
}

Remarques: Si la classe de base ou des 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 à moins que votre code ne tente d'utiliser l'opérateur d'affectation (alors seulement une erreur de compilation).

Destructor

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 une copie) est déclaré, le constructeur par défaut n'est pas implémenté par le compilateur.
  • Si le constructeur de la 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 n'en générera pas.

En examinant 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)
{}

Autres conseils

Le compilateur fournit un constructeur de copie sauf si vous déclarez (remarque: pas dé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).

Il en va de même pour l'opérateur d'affectation et le destructeur, BTW. Il en va différemment pour le constructeur par défaut: Ce n'est fourni par le compilateur que si vous ne déclarez aucun autre constructeur vous-même.

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 qui le contient. Si l'un des types de membre n'offre pas lui-même de constructeur de copie, le constructeur de copie potentiel de la classe contenante ne peut pas être généré. Il peut encore être possible d’en écrire un manuellement, si vous pouvez choisir un moyen approprié d’initialiser la valeur du membre qui ne peut pas être construit en copie, peut-être en utilisant l’un de ses autres constructeurs.

Le constructeur de copie par défaut crée un superficiel . 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 mémoire.

Le compilateur générera les constructeurs nécessaires pour vous.

Cependant, dès que vous définissez vous-même un constructeur de copie, le compilateur renonce à tout générer pour cette classe et donnera une 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 ou de construire une copie par défaut, Foo génère une erreur, car Baz n'est pas constructible pour la copie et le compilateur ne peut pas générer le constroctor par défaut et copier pour Foo.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top