La semántica de movimiento, introducida en C++11, es una característica fundamental que optimiza el rendimiento al permitir la transferencia de recursos en lugar de su copia.
Problema con la copia tradicional
Antes de C++11, asignar un objeto temporal a otro implicaba una copia profunda: asignar nueva memoria, duplicar datos y liberar el temporal. Esto es ineficiente cuando el objeto temporal está a punto de destruirse.
Conceptos clave: valores izquierdos y derechos
Para entender la semántica de movimiento, es esencial distinguir entre valores izquierdos (lvalues) y derechos (rvalues). Un valor izquierdo tiene un nombre y dirección persistente, como una variable. Un valor derecho es temporal y sin nombre, como el resultado de una expresión.
La semántica de movimiento permite "robar" recursos de objetos derechos, evitnado copias innecesarias.
Implementación con referencias de valor derecho y constructores de movimiento
Se implementa mediante referencias de valor derecho (&&) y constructores de movimiento. A continuación, un ejemplo modificado con un clase que gestiona memoria dinámica:
class AlmacenDinamico {
int* buffer;
size_t capacidad;
public:
// Constructor de copia (copia profunda)
AlmacenDinamico(const AlmacenDinamico& origen) {
capacidad = origen.capacidad;
buffer = new int[capacidad];
for (size_t i = 0; i < capacidad; ++i) {
buffer[i] = origen.buffer[i];
}
}
// Constructor de movimiento (referencia de valor derecho)
AlmacenDinamico(AlmacenDinamico&& fuente) noexcept {
buffer = fuente.buffer;
capacidad = fuente.capacidad;
fuente.buffer = nullptr;
fuente.capacidad = 0;
}
~AlmacenDinamico() { delete[] buffer; }
};
El constructer de movimiento toma el puntero del objeto fuente y lo establcee como nulo, transfiriendo la propiedad.
Uso explícito con std::move
std::move convierte un valor izquierdo en un valor derecho, permitiendo la transferencia de recursos de variables que ya no se necesitan:
AlmacenDinamico original;
// Uso de original...
AlmacenDinamico nuevo = std::move(original);
// original ahora está en un estado válido pero no definido; evite usarlo.
Beneficios principales
La semántica de movimiento mejora drásticamente el rendimiento para contenedores como std::vector, donde el intercambio de punteros es O(1) en lugar de O(n) para copias. También permite la transferencia de recursos exclusivos, como std::unique_ptr, que prohíben la copia pero permiten el movimiento.