2010-09-06 20 views
18

Si el operator= está definido correctamente, ¿está bien utilizar lo siguiente como copia de constructor?Implementando el constructor de copia en términos de operador =

MyClass::MyClass(MyClass const &_copy) 
{ 
    *this = _copy; 
} 
+9

Utilice el [lenguaje de copiar y de intercambio] (http://stackoverflow.com/questions/3279543/what-is-the-copy-and- swap-idioma). – GManNickG

+0

Normalmente, el operador de asignación de copia hará una limpieza. Si su clase tiene un puntero a la memoria asignada dinámicamente, lo primero que debe hacer el operador de copiado es liberar esa memoria. Esta implementación del constructor de copia le daría al operador de asignación de copia un puntero colgante, que no desea eliminar. – Kevin

+0

Incluso si usa punteros inteligentes (en cuyo caso eliminar no sería un problema), aún estaría inútilmente construyendo y asignando todas las variables miembro. Solo usa copiar y cambiar. – Kevin

Respuesta

27

Si todos los miembros de MyClass tienen un constructor predeterminado, sí.

Tenga en cuenta que por lo general es al revés:

class MyClass 
{ 
public: 
    MyClass(MyClass const&);  // Implemented 
    void swap(MyClass&) throw(); // Implemented 
    MyClass& operator=(MyClass rhs) { rhs.swap(*this); return *this; } 
}; 

pasamos por valor de operator= para que el constructor de copia se llama. Tenga en cuenta que todo es excepcionalmente seguro, ya que se garantiza que el swap no arrojará (debe asegurarse esto en su implementación).

EDITAR, conforme a lo solicitado, acerca de las cosas de llamada por valor: El operator= se podría escribir como

MyClass& MyClass::operator=(MyClass const& rhs) 
{ 
    MyClass tmp(rhs); 
    tmp.swap(*this); 
    return *this; 
} 

C estudiantes ++ se les dice generalmente para pasar las instancias de clase por referencia debido a que el constructor de copia se llama a si se pasan por valor En nuestro caso, tenemos que copiar rhs de todos modos, por lo que pasar de valor está bien.

Por lo tanto, la operator= (primera versión, llamada por valor) se lee:

  • hacer una copia de rhs (a través del constructor de copia, llamada automática)
  • Intercambia su contenido con *this
  • Retorno *this y deje que rhs (que contiene el valor anterior) se destruya al salir del método.

Ahora, tenemos una bonificación adicional con este call-by-value. Si el objeto que se pasa al operator= (o cualquier función que obtiene sus argumentos por valor) es un objeto temporal , el compilador puede (y generalmente no lo hace) copiar. Esto se llama copia elision.

Por lo tanto, si rhs es temporal, no se realiza ninguna copia. Nos quedamos con:

  • Intercambiar this y rhs contenidos
  • Destruye rhs

Así paso por valor es, en este caso más eficiente que el paso por referencia.

+1

En realidad, no importa si MyClass tiene un constructor predeterminado. Solo si los miembros de datos y las clases base tienen uno ... –

+0

sí, tiene razón. –

+0

Ok, gracias. Estaba haciendo esto para evitar la duplicación de código en la implementación de 'operator =' y el constructor de copia. Con el modismo copiar y cambiar, el código se duplica en el constructor de copia y en el método 'swap'. ¿Estoy en lo cierto? – gregseth

5

Esta implementación implica que los constructores por defecto para todos los miembros de datos (y clases base) están disponibles y accesibles desde MyClass, porque se llamarán primero, antes de realizar la asignación. Incluso en este caso, tener esta llamada extra para los constructores puede ser costoso (dependiendo del contenido de la clase).

Todavía me apegaría a la implementación separada del constructor de copias a través de la lista de inicialización, incluso si eso significa escribir más código.

Otra cosa: esta implementación podría tener efectos secundarios (por ejemplo, si tiene miembros dinámicamente asignados).

0

Yo diría que esto no está bien si MyClass asigna memoria o es mutable.

+3

Si no es mutable, entonces no tendrá un 'operator =' - eso es una función de mutación. ¿O no quiero decir lo mismo por mutable que tú? –

11

Es más recomendable implementar operator = en términos de un constructor de copia segura de excepción. Vea el Ejemplo 4. en esto de Herb Sutter para una explicación de la técnica y por qué es una buena idea.

http://www.gotw.ca/gotw/059.htm

1

Mientras que el resultado final es el mismo, los miembros son de primera defecto inicializado, sólo se copian después de eso.

Con miembros 'caros', es mejor copiar-construir con una lista de inicializadores.

struct C { 
    ExpensiveType member; 

    C(const C& other): member(other.member) {} 
}; 



}; 
+0

No puede inicializar la construcción exterior. – GManNickG

+0

@GMan: maldición. Quería escribir el constructor de copia, no la tarea. Lo siento por eso. – xtofl

+0

No hay problema, eliminado -1. :) – GManNickG

0

sí.

personalmente, si su clase no tiene punteros aunque no sobrecargaría el operador igual o escribiría el constructor de copia y dejaría que el compilador lo haga por usted; implementará una copia superficial y sabrá con seguridad que se copian todos los datos de los miembros, mientras que si sobrecarga el = op; y luego agrega un miembro de datos y luego olvida actualizar la sobrecarga, tendrás un problema.

0

@Alexandre - No estoy seguro de pasar el valor en el operador de asignación. ¿Cuál es la ventaja que obtendrá llamando al constructor de copia allí? ¿Esto va a atar al operador de asignación?

P.S. No sé cómo escribir comentarios. O puede ser que no tengo permiso para escribir comentarios.

+0

De acuerdo, edito mi respuesta para proporcionar más detalles. –

+0

La referencia habitual es http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/. Todavía no estoy convencido de que las conclusiones sean siempre correctas. –

+0

@Steve: para 'operator =', ya que tiene que hacer la copia de todos modos, no puede ser peor. –

0

Es es técnicamente correcto, si tiene un operador de asignación de trabajo (operador de copia).

Sin embargo, se debe preferir la copia-y-canje porque:

  • seguridad Excepción es más fácil con la copia de intercambio
  • separación más lógica de las preocupaciones:
    • La copia-ctor es aproximadamente asignando los recursos que necesita (para copiar las otras cosas).
    • La función de intercambio es (en su mayoría) solamente sobre el intercambio de "asas" internos y no necesita hacer de recursos (de) la asignación
    • El destructor se trata de cancelación de asignación de recursos
    • de copiar y de intercambio combina de forma natural estos tres función en el operador de asignación/copia
Cuestiones relacionadas