assignment - Constructor de copia implícita C++ para una clase que contiene otros objetos
copy constructor struct c++ (5)
Sé que el compilador 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 compilador en Foo
llamará al constructor generado por el compilador en Bar
para copiar sobre la bar
, que luego llamará al constructor de copia generado por el compilador en Baz
?
El constructor de copia predeterminado de C ++ crea una copia shallow . 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 compilador generará los constructores necesarios para usted.
Sin embargo, tan pronto como defina usted mismo un constructor de copias, el compilador 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 compilable y el compilador no puede generar el constroctor predeterminado y de copia para Foo.
El compilador proporciona un constructor de copia a menos que declare (observe: no defina ) uno usted mismo. El constructor de copia generado por el compilador 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 compilador solo si usted no declara ningún otro constructor usted mismo.
Sí, el constructor de copia generado por el compilador 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.
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 compilador.
- 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 compilació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 compilació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 compilació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 compilador no implementa el constructor predeterminado.
- Si el constructor de copia se declara, el compilador no generará uno.
- Si se declara el operador de asignación, el compilador no generará uno.
- Si se declara un destructor, el compilador 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)
{}