2009-04-23 14 views
11

Recientemente descubrí que cuando tengo punteros dentro de una clase, necesito especificar un constructor de Copia.Copiar constructor con punteros

Para saberlo, he hecho el siguiente código simple. Se compila, pero me da error de tiempo de ejecución cuando se realiza el constructor de copia.

Estoy tratando de copiar solo el valor del puntero del objeto copiado, pero evitando asignar la misma dirección.

Entonces, ¿qué sucede aquí?

class TRY{ 
     public: 
     TRY(); 
    ~TRY(); 
     TRY(TRY const &); 

     int *pointer; 

     void setPointer(int); 
    }; 


    void TRY::setPointer(int a){ 
     *pointer = a; 

     return; 
    } 


    TRY::TRY(){} 


    TRY::~TRY(){} 


    TRY::TRY(TRY const & copyTRY){ 
     int a = *copyTRY.pointer; 
     *pointer = a; 
    } 



    int main(){ 

     TRY a; 
     a.setPointer(5); 

     TRY b = a; 

     b.setPointer(8); 

     cout << "Address of object a = " << &a << endl; 
     cout << "Address of object b = " << &b << endl; 

     cout << "Address of a.pointer = " << a.pointer << endl; 
     cout << "Address of b.pointer = " << b.pointer << endl; 

     cout << "Value in a.pointer = " << *a.pointer << endl; 
     cout << "Value in b.pointer = " << *b.pointer << endl; 

     return 0; 
    } 

voy a utilizar este concepto para otras clases con un montón de punteros en el mismo, en las que necesito para copiar todos los valores desde el objeto a la otra. La copia es necesaria inicialmente para este código, por lo que me gustaría mantener la posibilidad de copiar (no ocultaré el constructor de copia como privado).

Además, la clase real que necesito implementar tiene como 10 punteros, y podría estar cambiando con el tiempo. ¿No hay una manera un poco más inteligente para tener un constructor copia profunda en C++? ...

Respuesta

12

Con la declaración int* pointer acaba de definir un puntero, pero no ha asignado ninguna memoria. Primero, debe hacer que apunte a una ubicación de memoria adecuada asignando memoria como esta: int* pointer = new int. Luego, en el constructor de copias, nuevamente debe asignar la memoria para el objeto copiado. Además, no olvide liberar la memoria utilizando delete en el destructor.

espero que este ejemplo ayuda a:

class B 
{ 

public: 
    B(); 
    B(const B& b); 
    ~B(); 
    void setVal(int val); 

private: 
    int* m_p; 
}; 

B::B() 
{ 
    //Allocate the memory to hold an int 
    m_p = new int; 

    *m_p = 0; 
} 

B::B(const B& b) 
{ 
    //Allocate the memory first 
    m_p = new int; 

    //Then copy the value from the passed object 
    *m_p = *b.m_p; 
} 

B::~B() 
{ 

    //Release the memory allocated 
    delete m_p; 
    m_p = NULL; 
} 

void B::setVal(int val) 
{ 
    *m_p = val; 
} 
+3

No olvides eliminar m_p antes de asignarle un nuevo objeto. – xtofl

+0

No he definido el operador de asignación, solo es un constructor de copia. Por lo tanto, no es necesario eliminar m_p. – Naveen

+3

En un constructor, m_p inicialmente no estará definido a menos que lo coloque explícitamente en la lista de inicializadores. Si intenta eliminar un puntero indefinido, sucederán cosas malas. –

1

si tiene un puntero a un tipo regular entonces

A::A(const A& a): 
    pointer_(new int(*a.pointer_)) 
{ 
} 

si tiene un indicador a una clase base entonces

A::A(const &a): 
    pointer_(a.pointer_->clone()) 
{ 
} 

Clon es una implementación de un prototype pattern

no se olvide de eliminar el puntero en el destructor

A::~A() 
{ 
    delete pointer_; 
} 

para fijar su ejemplo

TRY::TRY(TRY const & copyTRY){ 
    int a = *copyTRY.pointer; 
    pointer = new int(a); 
} 
3

Si usted quiere hacer una copia profunda, que por supuesto también debe asignar nueva memoria para contener los valores. Si el original tiene un puntero a un int, y no desea que la copia use el mismo valor de puntero, debe asignar memoria nueva para mantener un int y luego copiar el valor allí.

Su ejemplo no es muy claro, no muestra la implementación de su constructor de copia, o cómo se inicializa el miembro pointer.

1

Su problema es en esta línea aquí:

*pointer = a; 

Todas las cosas que normalmente sucede en su constructor por defecto no ha sucedido todavía, incluida la asignación de memoria para *pointer.

La solución es asignar memoria para un número entero.Puede usar malloc y friends o new para esto, pero asegúrese de que sea el mismo método que usa en su constructor predeterminado, porque solo obtiene un destructor y las llamadas tienen que coincidir.

1

Si una copia para miembros (poco profunda) está bien, entonces no tiene que hacer nada. Si desea una copia profunda, debe asignar nuevo espacio de almacenamiento para copias de todos los miembros.

0

Al escribir un Copy Constructor, debe asignar memoria para todos los miembros. En su caso:

TRY::TRY(TRY const & copyTRY){ 
    pointer = new int(*(copyTry.pointer)); 
} 

operador = es de alguna manera similar, pero sin asignación de memoria.

TRY& operator=(TRY const& otherTRY){ 
     this->a = *(otherTry.pointer) 
     return *this 
} 
8

he descubierto recientemente que cuando tengo punteros dentro de una clase, necesito para especificar un constructor de copia.

No es del todo cierto. Cuando tenga punteros en su clase y asigne la memoria usando new, tendrá que preocuparse por el constructor de copias. Además, no olvides el operador de asignación y el destructor. Debe eliminar la memoria asignada usando delete.

Se llama Law Of The Big Three.

Ejemplo:

~Matrix(); //Destructor 
    Matrix(const Matrix& m); //Copy constructor 
    Matrix& operator= (const Matrix& m); //Assignment operator 
1

Recientemente he descubierto que cuando tengo punteros dentro de una clase, necesito para especificar un constructor de copia

Más a menudo que no se trata de una buena idea simplemente deshabilitarlo al declararlo (y el operador de asignación) privado y no implementarlo.

+0

¿Por qué? Está copiando algo conceptualmente "prohibido" en la programación, o es porque la copia puede provocar problemas en los lenguajes OO? – Biga

+0

No está "prohibido", simplemente no tiene sentido y/o es caro. –

0

Más de las veces, si USTED necesita escribir un constructor de copias o un operador de asignación, está haciendo algo mal. Deje los constructores de copia y los operadores de asignación a los implementadores de la biblioteca estándar. Componga sus clases de elementos asignables y que ya se pueden copiar y no tendrá que escribir el suyo propio.

Por ejemplo, tal vez ese miembro int * debería ser un std :: vector en su lugar.

Si no puede hacer que la clase sea copiable/asignable por defecto, tal vez pueda hacerlo no copiable/asignable declarando, pero no implementando, un constructor de copia privada y un operador de asignación.

Solo si ninguna de las opciones anteriores son factibles, debe implementar su propio constructor de copias u operador de asignación.

Cuestiones relacionadas