Pregunta

Sé que el compilador a veces proporciona un constructor de copia predeterminado si no se implementa. Estoy confundido acerca de lo que 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 barra , que luego llamará al compilador creador de copia generado en Baz ?

¿Fue útil?

Solución

Foo f1;
Foo f2(f1);

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

Si define una clase como esta:

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

El compilador definirá los siguientes métodos.

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

Constructor: Predeterminado:

En realidad hay dos constructores por defecto.
Uno se usa para inicialización cero mientras que el otro se usa para inicialización de valores . El uso 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 algún miembro no tiene un constructor predeterminado válido válido, 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 algún miembro no tiene un constructor de copia visible válido, 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 algún miembro no tiene un operador de asignación viable válido, el operador de asignación no se puede generar. 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).

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 se declara cualquier constructor (incluida la copia), el compilador no implementa el constructor predeterminado.
  • Si se declara el constructor de copia, 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.

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

Otros consejos

El compilador proporciona un constructor de copia a menos que usted declare (nota: no defina ) uno 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: el compilador lo proporciona solo si usted no declara ningún otro constructor.

Sí, el constructor de copia generado por el compilador realiza una copia de miembros, en el orden en que se declaran los miembros en la clase contenedora. Si alguno de los tipos de miembros no ofrece un constructor de copia, no se puede generar el posible constructor de copia de la clase contenedora. Aún puede ser posible escribir uno manualmente, si puede decidir sobre algún medio apropiado para inicializar el valor del miembro que no se puede construir con copia, tal vez utilizando uno de sus otros constructores.

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

El compilador generará los constructores necesarios para ti.

Sin embargo, tan pronto como usted mismo define un constructor de copia, el compilador deja de generar cualquier cosa para esa clase y le dará un error si no tiene los constructores adecuados definidos.

Usando tu ejemplo:

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

Intentando crear una instancia de copia o copia predeterminada, Foo generará un error ya que Baz no puede construirse por copia y el compilador no puede generar el constructor predeterminado y de copia para Foo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top