2010-03-05 14 views

Respuesta

10

En caso de que su clase contenga solo vectores/objetos de cadena como sus miembros de datos, no necesita implementarlos. Las clases C++ STL (como vector, cadena) tienen su propio controlador de copia, operador de asignación sobrecargado y destructor.

Pero en el caso de que su clase asigne dinámicamente la memoria en el constructor, una copia ingenua y superficial generará problemas. En ese caso, deberá implementar copy ctor, operador de asignación sobrecargado y destructor.

0

necesita proporcionarlos si los necesita. o posibles usuarios de tus clases. destructor es siempre debe, y los constructores de copia y el operador de asignación se crean automáticamente por el compilador. (MSVC al menos)

+1

Destructor también es automático (el compilador no los hará * virtuales *, sin embargo, pero este es otro problema). – visitor

5

La regla general dice: si necesita una de ellas, las necesita todas. Sin embargo, no todas las clases los necesitan. Si su clase no posee recursos (memoria, en particular), estará bien sin ellos. Por ejemplo, una clase con un solo constituyente string o vector realmente no los necesita, a menos que necesite algún comportamiento especial de copia (el valor predeterminado solo copiará los miembros).

+0

En lugar de decir "no todas las clases los necesitan", ¿no sería más exacto decir que "retener el constructor de copia predeterminado, el destructor y el operador de asignación estarán bien"? (Es decir, no tendrá que anular los valores predeterminados con sus propias implementaciones). – DavidRR

4

El constructor de copia predeterminado copiará el vector si está declarado por valor. Tenga cuidado si almacenó punteros en su vector, en tal caso, debe proporcionar un comportamiento específico para copiar/asignar/destruir para evitar pérdidas de memoria o eliminación múltiple.

1

esos contenedores necesitarán un elemento de "copia compilable", y si no proporciona el constructor de copia, llamará al constructor de copia predeterminado de su clase deduciendo de los miembros de su clase (copia superficial).

fácil explicación acerca de constructor de copia por defecto está aquí: http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html

es así que con el destructor, el recipiente necesita tener acceso a su destructor o el destructor de la clase por defecto si no se proporciona uno (es decir, que lo hará. no funciona si declara su destructor como privado)

+0

Encontró la información en el enlace suministrado de gran ayuda. – DavidRR

0

Cuando tenga una clase que requiera copias profundas, debe definirlas.

Específicamente, cualquier clase que contiene punteros o referencias debe contener ellos, tales como:

class foo { 
private: 
    int a,b; 
    bar *c; 
} 

Subjetivamente, diría siempre definirlos, como el comportamiento por defecto proporcionada por la versión del compilador generado puede no ser lo que esperar/querer

+1

Puede ser que sea mejor decir: si la clase * posee * el recurso. Tal como está, esa instancia 'bar'' c' está apuntando a que podría ser propiedad y controlada en otro lugar, y 'foo' es solo un usuario que comparte el objeto. - Curiosamente, también recomendaría * no * definirlos si el valor predeterminado es correcto: es mucho más probable que cometa errores que el compilador y rompa copiando y asignando (y en el destructor no hay nada que hacer en primer lugar En ese caso). – visitor

+0

@visitor: vea la respuesta de lilburne, básicamente es la misma, pero más detallada por sus motivos, subjetivamente, creo que tiene razón en el dinero. –

+0

Naturalmente los necesitas si quieres algo más que una copia superficial y personal. Pero no estoy del todo convencido de por qué debería hacerlo manualmente para una copia de miembro (que es la mayoría de las clases para mí, si se van a copiar en primer lugar) - si eso no es lo que espera, quizás usted esperar semánticas muy extrañas de la copia. - Tal vez una razón objetiva para escribir el operador de asignación manualmente es para que pueda dar garantías de excepción más fuertes (lhv no cambiado, no solo no se filtró memoria), pero supongo que sería muy complicado (necesidad de deshacer cambios) para ser universal. – visitor

0

No para cadenas o vectores, ya que los constructores/destructores triviales, etc., lo harán bien.

Si su clase tiene punteros a otros datos y necesita copias en profundidad, o si su clase contiene un recurso que tiene que ser desasignado o debe copiarse de una manera especial.

2

No, pero hay una serie de razones por las que no debe permitir que el compilador genere automáticamente estas funciones.

Según mi experiencia, siempre es mejor definirlas usted mismo y adquirir el hábito de asegurarse de que se mantengan cuando se cambie la clase. En primer lugar, es posible que desee establecer un punto de interrupción cuando se llame a un ctor o dtor en particular. Además, al no definirlos, puede producirse una saturación del código, ya que el compilador generará llamadas en línea al miembro ctor y dtor (Scott Meyers tiene una sección sobre esto).

También a veces desea rechazar las asignaciones y las funciones de copia predeterminadas. Por ejemplo, tengo una aplicación que almacena y manipula grandes bloques de datos. Rutinariamente tenemos el equivalente de un vector STL que contiene millones de puntos 3D y sería un desastre si permitiéramos que esos contenedores se construyeran. Entonces los operadores de ctor y asignación se declaran privados y no definidos. De esta forma si alguien escribe

class myClass { 
    void doSomething(const bigDataContainer data); // not should be passed by reference 
} 

entonces obtendrán un error de compilación. Nuestra experiencia es que un método explicit become() o clone() es mucho menos propenso a errores.

Así que en general hay muchas razones para evitar las funciones de compilación auto generadas.

+0

debería 'no' ser' nota'? – Bill

+1

"adquiera el hábito de asegurarse de que se mantengan cuando cambie la clase". Esa es una pesadilla de mantenimiento innecesaria. – fredoverflow

+0

¿No debería tener pruebas unitarias para sus ctors, etc. para verificar la inicialización correcta? ¿No deberías considerar todas las implicaciones de agregar miembros de datos a las clases? Si agrega una nueva cadena a una clase, ¿cuál es el impacto en la saturación del código en todos los métodos que la utilizan y en todas las clases que pueden contener instancias de ella? Al haber agregado un nuevo miembro, ¿no tiene que reconsiderar si ya es viable permitir la autogeneración? Mientras se pregunta acerca de todas esas cosas que se suman al copy-ctor y op = es mínimo. – lilburne

2

Puedo pensar en algunos casos cuando necesite escribir su propio Big Three. Todos los contenedores estándar saben cómo copiarse y destruirse a sí mismos, por lo que no es necesario que los escriba. He aquí cómo saber cuándo lo hace:

¿Mi clase posee algún recurso?

La semántica de copia por defecto para punteros es copiar el valor del puntero, no a lo que apunta. Si necesita copiar profundamente algo, incluso si está almacenado dentro de un contenedor estándar, necesita escribir su propio constructor de copia y operador de asignación. También necesita escribir su propio destructor para liberar adecuadamente esos recursos.

¿Podría alguien heredar de mi clase?

Las clases base necesitan un destructor. Herb Sutter recomienda hacerlos ya sea public y virtual (caso más común) o protected y no virtual, dependiendo de lo que quiera hacer con ellos. El destructor generado por el compilador es público y no virtual, por lo que tendrá que escribir el suyo, incluso si no tiene ningún código. (Nota: esto no implica que tiene que escribir un operador de constructor de copia o cesión.)

¿Debo evitar que un usuario copiar objetos de mi clase?

Si no desea que el usuario copiar sus objetos (tal vez eso es demasiado caro), tiene que declarar los operadores constructor de copia y asignación sea protected o private. No tiene que implementarlos a menos que los necesite. (Nota: esto no implica que tenga que escribir un destructor.)

En pocas palabras:

Lo más importante es entender lo que el constructor generado por el compilador copia, operador de asignación, y el destructor harán. No necesita tenerles miedo, pero debe pensar en ellos y decidir si su comportamiento es apropiado para su clase.

Cuestiones relacionadas