2011-01-15 17 views
13

Después de leer sobre los constructores de copias y los operadores de asignación de copias en C++, traté de crear un ejemplo simple. Aunque aparentemente parece que el fragmento siguiente funciona, no estoy seguro de si estoy implementando el constructor de copias y el operador de asignación de copias de la manera correcta. ¿Podría señalar si hay errores/mejoras o un mejor ejemplo para comprender los conceptos relevantes?C++: implementar el constructor de copias y el operador de asignación de copias

class Foobase 
{ 
    int bInt; 

public: 
    Foobase() {} 

    Foobase(int b) { bInt = b;} 

    int GetValue() { return bInt;} 

    int SetValue(const int& val) { bInt = val; } 
}; 


class Foobar 
{ 
    int var;  
    Foobase *base;  

public: 
    Foobar(){} 

    Foobar(int v) 
    { 
     var = v;   
     base = new Foobase(v * -1); 

    } 

    //Copy constructor 
    Foobar(const Foobar& foo) 
    {  
     var = foo.var; 
     base = new Foobase(foo.GetBaseValue()); 
    } 

    //Copy assignemnt operator 
    Foobar& operator= (const Foobar& other) 
    { 
     if (this != &other) // prevent self-assignment 
     { 
      var = other.var; 
      base = new Foobase(other.GetBaseValue()); 

     } 
     return *this; 
    } 

    ~Foobar() 
    { 
     delete base; 
    } 

    void SetValue(int val) 
    { 
     var = val; 
    } 

    void SetBaseValue(const int& val) 
    { 
     base->SetValue(val); 
    } 

    int GetBaseValue() const 
    { 
     return(base->GetValue()); 
    } 

    void Print() 
    { 
     cout<<"Foobar Value: "<<var<<endl; 
     cout<<"Foobase Value: "<<base->GetValue()<<endl; 

    } 

}; 

int main() 
{ 
    Foobar f(10);  
    Foobar g(f); //calls copy constructor 
    Foobar h = f; //calls copy constructor 

    Foobar i; 
    i = f; 

    f.SetBaseValue(12); 
    f.SetValue(2);  

    Foobar j = f = z; //copy constructor for j but assignment operator for f 

    z.SetBaseValue(777); 
    z.SetValue(77); 

    return 1; 
} 

Respuesta

12

Su operador de asignación de copias está implementado incorrectamente. El objeto asignado a las filtraciones del objeto es base puntos a.

Su constructor por defecto también es incorrecto: se deja a ambos base y var sin inicializar, así que no hay manera de saber si cualquiera es válida y en el destructor, cuando se llama delete base;, pasan cosas malas.

La forma más fácil de implementar el constructor de copias y el operador de asignación de copias y saber que lo ha hecho correctamente es usar the Copy-and-Swap idiom.

+1

+1 buen punto, el idioma de copiar y cambiar es esencial para tener una implementación segura de excepción que garantice que el objeto permanezca en un estado consistente. – jdehaan

+0

¿La memoria no será liberada por el destructor '~ Foobar()'? – blitzkriegz

+0

@Mahatma: '~ Foobar()' no se invocará; el objeto nunca se destruye, se asigna a. –

2

Solo Foobar necesita un constructor de copia personalizada, un operador de asignación y un destructor. Foobase no necesita uno porque el comportamiento predeterminado que proporciona el compilador es suficiente.

En el caso de Foobar tiene una fuga en el operador de asignación. Puede solucionarlo fácilmente liberando el objeto antes de asignarlo, y eso debería ser lo suficientemente bueno. Pero si alguna vez agrega un segundo miembro del puntero al Foobar, verá que allí es cuando las cosas se complican. Ahora, si tiene una excepción al asignar el segundo puntero, necesita limpiar correctamente el primer puntero que asignó, para evitar daños o fugas. Y las cosas se vuelven más complicadas que eso de forma polinómica a medida que agrega más miembros de puntero.

En cambio, lo que quiere hacer es implementar el operador de asignación en términos del constructor de copia. Luego, debe implementar el constructor de copias en términos de una función de intercambio no lanzado. Lea sobre la copia & Cambio de idioma para más detalles.

Además, el constructor predeterminado Foobar no inicializa por defecto los miembros. Eso es malo, porque no es lo que el usuario esperaría. El puntero del miembro apunta a una dirección arbitraria y el int tiene un valor arbitrario. Ahora, si usas el objeto que el constructor creó, estás muy cerca de Undefined Behavior Land.

+0

Si hago una 'base de eliminación;' antes de la nueva asignación para liberar la memoria original, aparece el error 'double free or corruption'. Veré el idioma. No recibí la última parte sobre el problema con el constructor predeterminado vacío. Debería haber puesto 'var = SOMEVALUE;' y 'base = NULL;' allí ya que en mi ejemplo, uso el constructor parametrizado. – blitzkriegz

+0

La ausencia de paréntesis en * new-expression * solo hace una diferencia para los tipos sin constructores, que ninguno de los tipos aquí son. * La inicialización por defecto * de tipos con al menos un constructor definido por el usuario genera una llamada al constructor predeterminado. –

+0

Pedadicamente, el constructor predeterminado HACE * default-initialize * los miembros (al no tener un * ctor-initializer *, no se mencionan en ningún * mem-initializer-id *, y el estándar especifica que son * default-initialized * en este caso). Pero la inicialización predeterminada de un 'int' deja el estado no especificado. –

0

que tienen un parche muy simple para usted:

class Foobar 
{ 
    int var;  
    std::unique_ptr<FooBase> base; 

... 

que debería empezar.

La conclusión es:

  1. No llame delete en su código (Los expertos ven el punto 2)
  2. No llame delete en su código (ya saben mejor ...)
+0

Según la antigüedad de la biblioteca C++ proporcionada con el compilador, podría ser 'std :: tr1 :: unique_ptr' ... –

Cuestiones relacionadas