2009-09-30 17 views

Respuesta

24

Desde un relacionado (pero no igual) pregunta - Why don't C++ compilers define operator== and operator!=?:

BS dijo lo siguiente sobre el constructor de copia por defecto en "El diseño y evolución de C++" (Sección 11.4.1 - El control de copia):

Personalmente considero desafortunado que las operaciones de copia estén definidas por defecto y prohibo copiar objetos de muchas de mis clases. Sin embargo, C++ heredó su asignación predeterminada y copió los constructores de C, y se utilizan con frecuencia.

Así que la respuesta es que se incluyó a regañadientes por BS para la compatibilidad hacia atrás con C (probablemente la causa de la mayoría de C++ 's verrugas, sino también probablemente la razón principal para C++ popularidad s).

Para mis propios fines, en mi IDE, el fragmento que uso para nuevas clases contiene declaraciones para un operador de asignación privado y un constructor de copia para que cuando genere una nueva clase no obtenga operaciones predeterminadas de asignación y copia - Tengo que elimine explícitamente la declaración de esas operaciones de la sección privada: si quiero que el compilador pueda generarlas para mí.

+7

una clase base como boost :: noncopyable logra lo mismo más sucintamente – philsquared

+0

Me gustaría que me gustara así: Proporcionar un ctor predeterminado de forma predeterminada, pero solo si no hay otro ctor. Si existe, uno puede hacer '= default' como Richard muestra para C++ 0x, y aún tiene un buen rendimiento. ¿Qué piensa usted al respecto? –

+1

@Phil - Estoy de acuerdo, pero mi fragmento actualmente todavía usa el método "pasado de moda" debido a la inercia/pereza (no está realmente roto, así que no hay que esforzarse en arreglar la variedad) y no tiene ninguna dependencia. El uso de una clase base privada no copiable significa que el archivo .cpp debe incluir #include "noncopyable.h". Puede que no sea una gran excusa, pero es lo que tengo. Tenga en cuenta que a menudo limpio esto para usar una base no copiable, porque creo que es una mejor manera. Realmente debería arreglar mi fragmento. –

1

¿Le ahorra tiempo? Si tiene una clase simple (es decir, si puede copiar fácilmente todos sus elementos de construcción), entonces no necesita escribir una.

0

No estoy seguro de cuál es la "línea oficial" (no tengo el libro de Strastroup cerca de mí) y estoy seguro de que alguien va a desenterrarlo.

Sin embargo, en la mayoría de los casos, las copias superficiales y la inicialización predeterminada son "suficientemente buenas", por lo que es mejor para el compilador que proporcionarlas para que el desarrollador las escriba de forma explícita.

Si el desarrollador escribió esta copia trivial, los constructores y los campos cambiarán posteriormente, corresponde a ese usuario realizar los cambios y podría haber errores graves si olvida (p. Ej., ¿Cómo se construyen estos campos?).

Al hacer que los usuarios escriban constructores de copia cuando realmente hay una necesidad de hacer algo sofisticado (como la copia profunda), se reduce la frecuencia de estos errores.

6

Porque de lo contrario, cuando pasa una instancia por valor, ¿cómo podría generar el compilador?

+13

Podría hacer que el compilador se queje de la falta de un constructor de copia, supongo. – Uri

+0

Entonces creo que tendrías problemas de compatibilidad con versiones anteriores. Una clase y una estructura son similares. En C++, una estructura no es más que una clase con métodos y atributos públicos por defecto. Esto significa que cuando declaras una estructura de un programa de C compilado como C++, hay un constructor de copia implícito que debes asumir que está ahí para compatibilidad con versiones anteriores. Mueva esto a las clases, y tiene la situación que conocemos. –

+0

Podría decir que una vez que se define cualquier ctor, ya no se proporciona una copiadora por defecto. Esto aún sería compatible con C. –

3

No sé por qué fue diseñado originalmente de esa manera. Pero como usuario, puedo decir por qué estoy contento de haberlo hecho.

  1. Si utiliza todo tipo RAII o POD, un constructor de copia va a hacer lo correcto
  2. constructores de copia no requieren pensamiento o mantenimiento, que sólo funcionan dada # 1

me siento de la misma manera sobre el operador de asignación predeterminado. La mayoría de las personas a) definen incorrectamente su operador de constructor/igual de copia o no lo mantienen. He eliminado muchos errores en el código C++ cambiando a tipos RAII y eliminando operadores codificados a mano/constructores de copia.

1

Si tiene un código struct utilizado, un constructor de copia predeterminado debe estar allí para preservar la semántica de copia de C struct.

0

Creo que está llegando en la dirección equivocada; si el compilador pudiera escribir un constructor de copia "predeterminado" completo y correcto para cada clase o estructura, entonces nadie se quejaría, ¿o sí? El problema es que el compilador no puede hacer eso de manera confiable, por lo que para esos casos necesita escribir uno propio, anulando el predeterminado.

3

Unos pocos puntos:

Es importante ser capaz de controlar si un objeto puede o no se puede copiar.

Si no desea que un objeto se pueda copiar, puede declarar el constructor de copia como privado (y en C++ 0x podrá decir =delete). Sin embargo, esto no cambia con su propuesta, el problema simplemente se revierte, es decir. podría prever una pregunta como: "¿Por qué el compilador no genera un constructor de copia predeterminado cuando sabe qué hacer?"

Esto también se abordarán en C++ 0x, ya que creo que hay una bandera =default que le permitirá especificar que:

class C { 
public: 
    C (C const &) = default; 
}; 

Es beneficioso para permitir que el compilador para poner en práctica la mejor versión posible de el constructor de copia.

Aparte de la facilidad de uso, hoy el compilador puede elegir la técnica más eficiente para copiar el objeto. Por ejemplo, podría simplemente usar memcpy con el objeto si es sabe que eso es seguro hacerlo.

Con su propuesta, para lograr una optimización similar en la actualidad, el compilador necesitaría analizar el cuerpo del constructor para verificar que no hace más que copiar someramente a todos los miembros. No solo esto no es trivial, generalmente solo puede ocurrir cuando el cuerpo del constructor está visible en todas las unidades de traducción.

En C++ 0x =default se soluciona esto.

No me sorprendería que, para C++ 0x, los compiladores y las herramientas de análisis estático comiencen a generar advertencias sobre "miembros predeterminados implícitos de estilo antiguo".

+0

El nuevo C++ 0x 'default' y' delete' para compiladores generados por el compilador y funciones especiales serán bienvenidas. Sin embargo, tenga en cuenta que para el caso copy-ctor mi entendimiento es que el compilador aún generará uno por defecto en todos los mismos casos que lo hace hoy - la palabra clave 'delete' es una manera útil y simple de evitar eso (en otras palabras , habrá poca necesidad de que se use 'default' para el copy-ctor). Pesky compatibilidad hacia atrás. –

+1

@Michael Burr: Sí, tienes razón. El comportamiento existente de los compiladores con respecto a los miembros predeterminados implícitos permanecerá sin cambios. Como dije en la respuesta, el único atisbo de esperanza que tienes es que '= delete' y '= default' son maná from heaven para los estándares de codificación (y, por supuesto, herramientas de análisis estático). Su presencia permite que las herramientas destaquen las clases que el desarrollador no tuvo la intención explícita de copiar (es decir, usar el constructor de copia predeterminado). –

+0

@Richard: eso sería una buena herramienta de diagnóstico. –

7

De The C++ Programming Language, Sección 11.3.4 copia

... para este tipo donde el constructor de copia por defecto tiene la semántica correctas, prefiero confiar en que por defecto. Es menos detallado que cualquier cosa que pueda escribir, y la gente debería entender el valor predeterminado. Además, los compiladores saben sobre el valor predeterminado y sus posibles oportunidades de optimización. Además, escribir la copia para miembros a mano es tedioso y propenso a errores para las clases con muchos miembros de datos.

Básicamente, he leído que como el constructor de copia por defecto le ahorra el esfuerzo, se salva de cometer errores causados ​​por el tedio, y ayuda a optimizar su código mediante la eliminación de la la tentación de optimizarlo con la mano (al permitir que el compilador hazlo).

+2

Ese dicho es bastante extenso de lo que dice en "Diseño y Evolución de C++", hmm. –

+0

@litb: Los dos pasajes parecen estar en desacuerdo. Tal vez Stroustup originalmente los incluyó a regañadientes, y luego los abrazó más tarde. –

+4

No creo que esté en conflicto con lo que se dice en D & E. Tenga en cuenta que la instrucción aquí está calificada por "donde el contructor de copia predeterminado tiene las sematicas correctas". Lo que se dice en D & E es que a menudo deshabilita explícitamente el editor de copiado generado automáticamente porque a menudo no tiene las palabras correctas, y que le hubiera gustado que el copiador predeterminado no se haya generado automáticamente (pero tenía las manos atadas) . En CPL dice que * si * solo va a copiar bits, deje que el compilador lo haga por usted. –

0

Como C++ no es basura, te obliga a realizar un seguimiento de todos los propietarios de punteros, lo que en la práctica es imposible de hacer, a menos que simplifiques el problema utilizando muchas copias para que al menos sepas quién posee la copia y, por cierto, hace que su programa sea más lento que uno recolectado. Los constructores de copia generados automáticamente son un clavo en el ataúd que se colocó allí para ocultar el enorme agujero al infierno que es nuevo y eliminar.

Cuestiones relacionadas