2011-02-17 15 views
8

Tengo entendido que cuando realiza una copia de una clase que define una variable de puntero, se copia el puntero, pero los datos a los que apunta el puntero no lo son.Copia de clase C++ (copia de puntero)

Mi pregunta es: ¿Se supone que la "copia del puntero" en este caso simplemente crea una instancia de un nuevo puntero (asignación de memoria dinámica) del mismo tipo? Por ejemplo, el nuevo puntero es simplemente una nueva asignación que contiene una dirección de memoria arbitraria y uno debe tener cuidado de apuntar ese nuevo puntero a la dirección de memoria adecuada.

Supongo que hay una respuesta bastante simple a esta pregunta, y pido disculpas por su naturaleza trivial, pero estoy tratando de entender los indicadores a un nivel más profundo y esto surgió sobre mis punteros de investigación en Internet.

Saludos,

Chad

+0

Los punteros y los datos que señalan están separados. La respuesta de Martin a continuación debería aclarar si no revisa esto para obtener información básica: http://www.cplusplus.com/doc/tutorial/pointers/ – AJG85

Respuesta

13

El puntero se copian simplemente como un valor - por lo que ambas clases se apuntan a la misma memoria original, ninguna nueva asignación se lleva a cabo. Copia superficial - esto es lo que hace el idioma por defecto.

Si necesita asignar nueva memoria y hacer una copia de los datos, debe hacerlo usted mismo en el constructor de copia. Copia profunda - tiene que hacer esto usted mismo

editar: Esta es una de las ventajas de C++, usted es libre de decidir cómo funciona la copia. Es posible que una copia del objeto que solo tiene acceso de lectura a la memoria pueda evitar el costo de copiar la memoria. También puede implementar clases que solo hacen una copia de los datos originales si el nuevo objeto necesita hacer una escritura.

+1

@Chad Kemp: Ver también 'boost :: shared_ptr' y * smart pointers *. También estudie la diferencia entre * copia superficial * y * copia profunda *. –

+0

Perfecto. Aprecio todas las respuestas! ¡Perfectamente claro! Gracias a todos. –

1

El puntero copiado apuntará a la misma dirección exacta. No hay nueva asignación.

0

Sí, los punteros simplemente contienen direcciones de memoria; si desea hacer una copia más profunda, debe codificarla usted mismo, en el constructor de copias.

Si siempre hace referencia al mismo tipo de datos a través de un puntero de la misma clase, y necesita copiar los datos junto con los objetos, también podría considerar hacer simplemente un miembro simple, no un puntero.

5

Primero, el puntero de su clase es estático (es decir, el compilador sabe que hay un puntero en esta clase y qué tamaño tiene, por lo que no se necesita asignación de memoria dinámica cuando se crea una instancia de la clase).

Si copia la clase (y no tiene un constructor de copia especial definido), el puntero en la nueva clase apuntará al mismo lugar en la memoria que el puntero en la clase anterior. Para aclarar:

#include <iostream> 

class A { 
    public: 
     int *p; 
}; 

int main() { 
    A a,b; 
    a.p = new int(10); 
    b = a; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 10 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 10 

    *(b.p) = 3; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 3 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 3 

    return 0; 
} 

Ahora bien, si desea asignar la nueva memoria al copiar, tiene que escribir un constructor de copia y un constructor de asignación de copia:

#include <iostream> 

class A { 
    public: 
     int *p; 

     A() : p(0) {} 
     A(const A& other) { // copy constructor 
      p = new int(*other.p); 
     } 

     A& operator=(const A& other) { // copy assignment constructor 
      // protect against self assignment 
      if (this != &other) { 
       if (p != 0) { 
        *p = *other.p; 
       } else { // p is null - no memory allocated yet 
        p = new int(*other.p); 
       } 
      } 
      return *this; 
     } 

     ~A() { // destructor 
      delete p; 
     } 
}; 


int main() { 
    A a,b; 
    a.p = new int(10); 
    b = a; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 10 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 10 

    *(b.p) = 3; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 10 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 3 

    return 0; 
} 

Al hacer esto también se debe escribir una destructor (consulte Rule of three), porque la memoria asignada en los constructores de asignación de copia/copia debe ser desasignada si la clase se destruye:

2

Los punteros no crean una instancia de la asignación de memoria dinámica. Los punteros y las asignaciones son cosas completamente diferentes.

Si copia un puntero que apunta a la memoria asignada dinámicamente, tiene dos punteros apuntando a la misma memoria asignada. Ya que lo ha copiado, ya apunta al bloque de memoria. Específicamente, si usa el constructor de copia generado por el compilador, el nuevo puntero apuntará exactamente al mismo puntero. No necesita hacer nada con eso si está bien.

Tiene el problema de cuándo liberar la memoria. Liberarlo dos veces normalmente causará daños en el montón, lo que es desagradable. No liberarlo causará una pérdida de memoria, que puede ser aceptable en algunas circunstancias. Liberarlo antes de que el otro puntero termine también causará problemas. Por esta razón, las personas que tienen múltiples apuntadores a la misma memoria a menudo van al proyecto Boost y usan su plantilla shared_ptr (que estará en el próximo estándar nuevo y está presente en la mayoría de los sistemas actualizados).

Si desea que cada puntero apunte a trozos de memoria separados, debe configurarlo construyendo su propio constructor de copia, y asignando un nuevo fragmento y copiando los datos necesarios en él. (También necesita escribir su propio operador de asignación, por los mismos motivos exactos, y su propio destructor para que pueda liberar la memoria. Hay una regla de oro, llamada Regla de Tres, que dice que si necesita escribir su propia copia constructor, operador de asignación o destructor, probablemente necesite escribirlos todos).