2012-02-22 8 views
9

En este punto, escribir el constructor de copia y el par de operadores de asignación está bien definido; una búsqueda rápida le llevará a muchos éxitos sobre cómo codificarlos adecuadamente.¿Cuál es la forma "correcta" de escribir Copiar/Mover/operador = trío en C++ 11?

Ahora que el constructor de movimientos ha entrado en la mezcla, ¿hay una nueva "mejor" manera?

+1

Hubo una buena [ASUNTA] (http://stackoverflow.com/questions/9322174/move-assignment-operator-and-if-this-rhs) que tal vez quiera consultar. –

+1

Esta pregunta es demasiado amplia. Debes reducirlo a un escenario específico. Como no hay una receta de cómo escribir el constructor de copia y el operador de asignación de una manera "bien definida" para todas y cada una de las clases. Lo mismo para tu pregunta. –

+0

¿Por qué es demasiado amplio? Hay un patrón generalmente aceptado para el constructor de copias + operador de asignación, ¿por qué no también para el constructor de movimientos? – moswald

Respuesta

12

Preferiblemente, pero serán = default;, ya que los tipos de miembro deben ser de la gestión de recursos tipos que ocultan los detalles del movimiento que, al igual que std::unique_ptr. Solo los implementadores de esos tipos de "bajo nivel" deberían molestarse en lidiar con eso.

Recuerde que solo necesita preocuparse por la semántica de movimiento si tiene un recurso externo (para su objeto). Es completamente inútil para tipos "planos".

+1

Ese es un punto válido. Supongo que podría haber calificado con "si fuera a escribir el suyo", pero su respuesta es muy correcta para las personas que no se dan cuenta de que no necesitan hacer esto. Sin embargo, en mi caso, tengo un recurso externo, que es lo que generó esta pregunta. :) – moswald

+1

Refactorización menor probablemente eliminaría ese problema, sin embargo. Lo pensare. No sé si el resultado final será más simple. – moswald

+0

Desafortunadamente, no todos los compiladores generan el operador/asignador de movimiento predeterminado. VC2010 no, no estoy seguro acerca de VC2011. – lapk

2

Esto es lo que se me ocurrió, pero no sé si existe una solución más óptima.

class MyClass 
{ 
    void Swap(MyClass &other) 
    { 
     std::swap(other.member, member); 
    } 

public: 
    MyClass() 
     : member() 
    { 
    } 

    MyClass(const MyClass &other) 
     : member(other.member) 
    { 
    } 

    MyClass(MyClass &&other) 
     : member(std::move(other.member)) 
    { 
    } 

    MyClass &operator=(MyClass other) 
    { 
     other.Swap(*this); 
     return *this; 
    } 

private: 
    int member; 
}; 
+0

¿Por qué haría que el intercambio fuera privado? – ronag

+0

@ronag: en mi clase del mundo real, no hay una necesidad real (todavía). Prefiero no exponer una API que alguien podría venir más tarde y usar, posiblemente incorrectamente (aunque no sé cómo podrían abusar de algo tan simple como el intercambio). – moswald

+4

Si se supone que 'MyClass' tiene estas semánticas, esta es probablemente la peor forma (la de peor rendimiento) de escribir miembros especiales y aún así tenerla como correcta. Lo siento, para ser tan directo, pero pensé que deberías saberlo. –

5

La mejor manera es dejar que el compilador los genere a todos. También fue el mejor enfoque en C++ 03 y si lograste hacer esto, tus clases de C++ 03 se convertirán automáticamente en "habilitadas para mover" cuando migres a C++ 11. La mayoría de los problemas de gestión de recursos pueden resolverse escribiendo solo los constructores no copiados y destructores de clases de gestión de un solo recurso y solo haciendo clases compuestas usando estos, además de punteros inteligentes (p. Ej. std::unique_ptr) y clases de contenedor para construir objetos más ricos. .

4

Usando sonido metálico/libC++:

#include <chrono> 
#include <iostream> 
#include <vector> 

#if SLOW_DOWN 

class MyClass 
{ 
    void Swap(MyClass &other) 
    { 
     std::swap(other.member, member); 
    } 

public: 
    MyClass() 
     : member() 
    { 
    } 

    MyClass(const MyClass &other) 
     : member(other.member) 
    { 
    } 

    MyClass(MyClass &&other) 
     : member(std::move(other.member)) 
    { 
    } 

    MyClass &operator=(MyClass other) 
    { 
     other.Swap(*this); 
     return *this; 
    } 

private: 
    int member; 
}; 

#else 

class MyClass 
{ 
public: 
    MyClass() 
     : member() 
    { 
    } 

private: 
    int member; 
}; 

#endif 

int main() 
{ 
    typedef std::chrono::high_resolution_clock Clock; 
    typedef std::chrono::duration<float, std::milli> ms; 
    auto t0 = Clock::now(); 
    for (int k = 0; k < 100; ++k) 
    { 
     std::vector<MyClass> v; 
     for (int i = 0; i < 1000000; ++i) 
      v.push_back(MyClass()); 
    } 
    auto t1 = Clock::now(); 
    std::cout << ms(t1-t0).count() << " ms\n"; 
} 

$ clang++ -stdlib=libc++ -std=c++11 -O3 -DSLOW_DOWN test.cpp 
$ a.out 
519.736 ms 
$ a.out 
517.036 ms 
$ a.out 
524.443 ms 

$ clang++ -stdlib=libc++ -std=c++11 -O3 test.cpp 
$ a.out 
463.968 ms 
$ a.out 
458.702 ms 
$ a.out 
464.441 ms 

Esto se parece a aproximadamente un 12% de diferencia de velocidad en esta prueba.

Explicación: Una de estas definiciones tiene un constructor de copia trivial y un operador de asignación de copia. El otro no. "Trivial" tiene un significado real en C++ 11. Significa que la implementación puede usar memcpy para copiar su clase. O incluso para copiar grandes matrices de tu clase. Entonces, lo mejor es hacer que tus miembros especiales sean triviales si puedes. Eso significa dejar que el compilador los defina. Aunque todavía puede declararlos con = default si lo prefiere.

+0

Howard, 'SLOW_DOWN' es simplemente una versión muy ineficiente. Ni siquiera se trata de una comparación con los constructores generados por el compilador, etc ... Aquí hay dos códigos en ideone.com: [first one] (http://ideone.com/WQVXK) utiliza la versión de OP 'T & operator (T)', [ second] (http://ideone.com/SXgVN) utiliza 'T & (T &&)' explícitamente definido y 'T & operator = (T &&)'. Compare la salida entre '-----------' ... – lapk

+1

@AzzA: estoy simplemente comparando la solución de OP con una que creo que es mejor. Nada más y nada menos. –

Cuestiones relacionadas