Constructor de copia implícita C ++ para una clase que contiene otros objetos

Sé que el comstackdor a veces proporciona un constructor de copia predeterminado si no se implementa usted mismo. Estoy confundido acerca de qué hace exactamente este constructor. Si tengo una clase que contiene otros objetos, ninguno de los cuales tiene un constructor de copia declarado, ¿cuál será el comportamiento? Por ejemplo, una clase como esta:

class Foo { Bar bar; }; class Bar { int i; Baz baz; }; class Baz { int j; }; 

Ahora si hago esto:

 Foo f1; Foo f2(f1); 

¿Qué hará el constructor de copia predeterminado? ¿El constructor de copia generado por el comstackdor en Foo llamará al constructor generado por el comstackdor en Bar para copiar sobre la bar , que luego llamará al constructor de copia generado por el comstackdor en Baz ?

 Foo f1; Foo f2(f1); 

Sí, esto hará lo que usted espera:
Se llama al constructor de copias f2 Foo :: Foo (Foo const &).
Esta copia construye su clase base y luego cada miembro (recursivamente)

Si defines una clase como esta:

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

Los siguientes métodos serán definidos por tu comstackdor.

  • Constructor (predeterminado) (2 versiones)
  • Constructor (Copia)
  • Destructor (predeterminado)
  • Operador de asignación

Constructor: Predeterminado:

En realidad, hay dos constructores predeterminados.
Uno se utiliza para zero-initialization mientras que el otro se utiliza para value-initialization . El utilizado depende de si usa () durante la inicialización o no.

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

Notas: Si la clase base o cualquier miembro no tiene un constructor predeterminado visible válido, entonces no se puede generar el constructor predeterminado. Esto no es un error a menos que su código intente usar el constructor predeterminado (entonces solo un error de tiempo de comstackción).

Constructor (Copia)

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

Notas: Si la clase base o cualquier miembro no tiene un constructor de copia visible válido, entonces no se puede generar el constructor de copia. Esto no es un error a menos que su código intente usar el constructor de copia (entonces solo un error de tiempo de comstackción).

Operador de Asignación

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

Notas: Si la clase base o cualquier miembro no tiene un operador de asignación viable válido, entonces no se puede generar el operador de asignación. Esto no es un error a menos que su código intente usar el operador de asignación (entonces solo un error de tiempo de comstackción).

Incinerador de basuras

 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 se declara cualquier constructor (incluida copia), el comstackdor no implementa el constructor predeterminado.
  • Si el constructor de copia se declara, el comstackdor no generará uno.
  • Si se declara el operador de asignación, el comstackdor no generará uno.
  • Si se declara un destructor, el comstackdor no generará uno.

Al mirar su código, se generan los siguientes constructores de copia:

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

El comstackdor proporciona un constructor de copia a menos que declare (observe: no defina ) uno usted mismo. El constructor de copia generado por el comstackdor simplemente llama al constructor de copia de cada miembro de la clase (y de cada clase base).

Lo mismo es cierto para el operador de asignación y el destructor, por cierto. Sin embargo, es diferente para el constructor predeterminado: esto lo proporciona el comstackdor solo si usted no declara ningún otro constructor usted mismo.

Sí, el constructor de copia generado por el comstackdor realiza una copia para miembros, en el orden en que se declaran los miembros en la clase contenedora. Si alguno de los tipos de miembro no ofrece un constructor de copia, no se puede generar el posible constructor de copia de la clase que lo contiene. Todavía puede ser posible escribir uno manualmente, si puede decidir sobre algún medio apropiado para inicializar el valor del miembro que no puede ser copiado, quizás usando uno de sus otros constructores.

El constructor de copia predeterminado de C ++ crea una copia superficial . Una copia superficial no creará nuevas copias de objetos a los que su objeto original pueda hacer referencia; los objetos viejos y nuevos simplemente contendrán punteros distintos a la misma ubicación de memoria.

El comstackdor generará los constructores necesarios para usted.

Sin embargo, tan pronto como defina usted mismo un constructor de copias, el comstackdor dejará de generar algo para esa clase y dará y obtendrá un error si no tiene los constructores apropiados definidos.

Usando tu ejemplo:

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

Al tratar de crear instancias o copiar por defecto, Foo arrojará un error ya que Baz no es comstackble y el comstackdor no puede generar el constroctor predeterminado y de copia para Foo.