2012-06-18 12 views
5

He buscado pero no puedo encontrar la respuesta a "Cuando" para usarlos. Sigo oyendo que es bueno porque me ahorra esa copia extra. Fui por ahí poniéndolo en cada clase que tenía pero de alguna manera no parecía tener sentido para algunas clases: S He leído innumerables tutoriales sobre LValues ​​y RValues ​​y std :: move vs. std :: copy vs. memcpy frente a memmove, etc. e incluso lee en throw() pero no estoy seguro de cuándo usar eso tampoco.Cuándo usar Mover Constructores/Asignaciones

Mi código es el siguiente:

struct Point 
{ 
    int X, Y; 

    Point(); 
    Point(int x, int y); 
    ~Point(); 

    //All my other operators here.. 
}; 

Entonces tengo una matriz de clase de que al igual que (cosa sorta RAII):

class PA 
{ 
    private: 
     std::vector<Point> PointsList; 

    public: 
     PA(); 
     //Variadic Template constructor here.. 
     ~PA(); 
     //Operators here.. 
}; 

¿Debo utilizar un constructor mover y copiar el constructor? Lo tenía en la clase Point pero me pareció raro, así que lo eliminé. Luego lo tuve en la clase PA, pero pensé que no haría mucho, así que también lo eliminé. Luego, en mi clase de mapas de bits mi compilador se quejaba de tener miembros de puntero, pero no sobrecarga por lo que hice:

//Copy Con: 
BMPS::BMPS(const BMPS& Bmp) : Bytes(((Bmp.width * Bmp.height) != 0) ? new RGB[Bmp.width * Bmp.height] : nullptr), width(Bmp.width), height(Bmp.height), size(Bmp.size), DC(0), Image(0) 
{ 
    std::copy(Bmp.Bytes, Bmp.Bytes + (width * height), Bytes); 
    BMInfo = Bmp.BMInfo; 
    bFHeader = Bmp.bFHeader; 
} 

//Move Con: 
BMPS::BMPS(BMPS&& Bmp) : Bytes(nullptr), width(Bmp.width), height(Bmp.height), size(Bmp.size), DC(0), Image(0) 
{ 
    Bmp.Swap(*this); 
    Bmp.Bytes = nullptr; 
} 

//Assignment: 
BMPS& BMPS::operator = (BMPS Bmp) 
{ 
    Bmp.Swap(*this); 
    return *this; 
} 

//Not sure if I need Copy Assignment? 

//Move Assignment: 
BMPS& BMPS::operator = (BMPS&& Bmp) 
{ 
    this->Swap(Bmp); 
    return *this; 
} 

//Swap function (Member vs. Non-member?) 
void BMPS::Swap(BMPS& Bmp) //throw() 
{ 
    //I was told I should put using std::swap instead here.. for some ADL thing. 
    //But I always learned that using is bad in headers. 
    std::swap(Bytes, Bmp.Bytes); 
    std::swap(BMInfo, Bmp.BMInfo); 
    std::swap(width, Bmp.width); 
    std::swap(height, Bmp.height); 
    std::swap(size, Bmp.size); 
    std::swap(bFHeader, Bmp.bFHeader); 
} 

Es esto correcto? ¿Hice algo malo o incorrecto? ¿Necesito tirar()? ¿Deberían mis operadores de asignación y movimiento ser los mismos? ¿Necesito una tarea de copia? Ahh tantas preguntas: c El último foro en el que pregunté no pudo responder a todas las preguntas, por lo que me quedé confundido. Finalmente, ¿debería usar unique_ptr para Bytes? (Que es una matriz de bytes/píxeles.)

Respuesta

7

Hay algunos excelentes del pensamiento sobre Scott Meyer's blog:

En primer lugar, no todas las solicitudes de copia puede ser reemplazado por movimientos. Solo las solicitudes de copia de rvalues ​​son elegibles para la optimización. En segundo lugar, no todos los tipos admiten operaciones de movimiento que son más eficientes que las operaciones de copia. Un ejemplo es un std :: array. En tercer lugar, incluso los tipos que admiten operaciones de movimientos eficientes pueden respaldarlos solo algunas veces. Caso en punto: std :: string. Admite movimientos, pero en los casos en que std :: string se implementa utilizando SSO (la optimización de cadena pequeña), ¡las cadenas pequeñas son tan caras de mover como copiar!

Tal vez, puede clasificar sus tipos en consecuencia y luego decidir qué todos necesitan la semántica de movimiento. Tenga en cuenta que existen restricciones en los operadores de asignaciones/operadores de movimiento de generación automática del compilador, por lo que sería aconsejable tenerlos en cuenta. Esto ayuda cuando está explícitamente especificando mover miembros.

Para las clases en las que no especifica mover miembros explícitamente, hay algunos puntos de molestia. También existe el problema explícito/implícito eliminado mover miembro que inhibe la copia de rvalues ​​. En el documento de Stroustrup titulado To Move or Not to Move se puede encontrar una fuente de problemas de gran valor con la generación implícita de miembros de movimiento.

En cuanto al manejo de excepciones con semántica de movimiento, sugeriría la publicación de Dave Abraham Exceptionally Moving.

Trataré de volver a esta respuesta con algunos ejemplos cuando tenga el tiempo. Con suerte, por el momento, los enlaces mencionados anteriormente te ayudarán a comenzar.

+0

Esto definitivamente me hizo comenzar y pensar. No respondió algunas de las preguntas para las que me hubiera gustado obtener respuestas específicas, pero es más que suficiente para comenzar a aprender por mí mismo. Muchas gracias. – Brandon

2

Si bien el movimiento es una muy buena herramienta en la bolsa que permite mucho más que la velocidad.Con el movimiento, mueves el objeto (obviamente) y no dejas nada (aparte de un cadáver vacío, o más exactamente, un objeto construido por defecto). Esto te obliga a pensar más detenidamente sobre la propiedad y el diseño del programa cuando se favorecen los objetos de movimiento. En lugar de tener acceso múltiple a algunos objetos o compartirlos, moverte te obliga a tener en cuenta quién tiene el objeto y cuándo.

Como Bjarne Stroustrup ha declarado anteriormente, debemos dejar de compartir todo y tener punteros en todas partes. Si utiliza punteros use unique_ptr y no shared_ptr a menos que desee compartir la propiedad (lo cual en muchos casos no es así). Unique_ptr y su solo movimiento (copia bien borrada de todos modos) constructor es un buen ejemplo de un objeto que debe proporcionar movimiento y nunca copiar.

Mover es genial y escribir constructores de movimiento es una muy buena idea, incluso mejor cuando msvc alcanza y permite a los decoradores eliminados/predeterminados en los compiladores generados (copia/asignación, etc.) del compilador. Los errores como un intento de acceder a un miembro previamente eliminado son muy útiles aquí, simplemente hacer que algunos constructores sean privados tiene una intención menos obvia para un desarrollador de código. En los casos en que la copia es correcta pero preferible, un compilador elegirá moverse cuando pueda (es decir, probar con vector.push_back que con algunos compiladores moverá o emplace_back si es razonable, comprando ganancias de rendimiento al instante), , por lo tanto, incluso en el constructor de copias objetos un constructor de movimiento definido puede seleccionarse automáticamente mejorando el rendimiento (ignorando todas las discusiones de SSO que están en pleno auge en este momento). This is a decent answer to peruse

Hay algunos hilos bastante pesados ​​en la lista de correo de impulso sobre la ventaja/desventaja de mover/copiar y pasar por valor/referencia, que son todo mucho hablar de problemas similares, si estás buscando más información.

+0

Definitivamente valoré ese enlace y la explicación. ¡Gracias a ti también! Link fue definitivamente útil. – Brandon

Cuestiones relacionadas