Menu
Coddy logo textTech

Constructor de movimiento

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 21 de 104.

Un constructor de movimiento transfiere recursos de un objeto temporal en lugar de copiarlos. Mientras que un constructor de copia duplica los datos, un constructor de movimiento "roba" los recursos, dejando al objeto de origen en un estado válido pero vacío. Esto evita copias profundas costosas cuando el objeto de origen está a punto de ser destruido de todos modos.

El constructor de movimiento toma una referencia rvalue, denotada por &&:

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    // Constructor de movimiento
    Buffer(Buffer&& other) noexcept {
        data = other.data;       // Robar el puntero
        size = other.size;
        other.data = nullptr;    // Dejar el origen en un estado válido
        other.size = 0;
    }
    
    ~Buffer() { delete[] data; }
};

La diferencia clave con respecto a la copia: no asignamos nueva memoria. Simplemente tomamos la propiedad de la memoria existente y establecemos el puntero del origen a nullptr para que su destructor no elimine nuestros datos.

Los constructores de movimiento se llaman al inicializar desde objetos temporales o al usar std::move():

Buffer createBuffer() {
    return Buffer(1000);    // Se usa el constructor de movimiento (valor de retorno)
}

Buffer b1(1000);
Buffer b2(std::move(b1));   // Movimiento explícito - b1 ahora está vacío

Marca los constructores de movimiento como noexcept cuando sea posible. Esto le indica al compilador que la operación no lanzará excepciones, lo que permite optimizaciones importantes en contenedores como std::vector.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de paquetes de datos que demuestre cómo los constructores de movimiento transfieren eficientemente la propiedad de los recursos. Crearás una clase DataPacket que gestiona un array de bytes asignado dinámicamente, e implementarás un constructor de movimiento que "robe" los datos en lugar de copiarlos.

Crearás dos archivos para organizar tu código:

  • DataPacket.h: Define una clase DataPacket que gestiona una carga útil (payload) de datos enteros. Tu clase debe tener:
    • Miembros privados: un puntero a un array de enteros (payload), un size para el número de elementos y un packetId (string) para identificar el paquete
    • Un constructor parametrizado que recibe un ID de paquete y un tamaño, asigna el array y lo llena con valores de 0 a size-1. Imprime "Packet <id> created with size <size>"
    • Un constructor de movimiento marcado como noexcept que transfiere la propiedad del payload desde el objeto origen. Imprime "Packet <id> moved". Recuerda dejar el origen en un estado vacío válido (nullptr, size 0)
    • Un destructor que libera la memoria si el puntero no es nulo, e imprime "Packet <id> destroyed"
    • Un método getSize() que devuelve el tamaño actual
    • Un método getId() que devuelve el ID del paquete
    • Un método getSum() que devuelve la suma de todos los elementos en el payload (devuelve 0 si el payload es nulo)
  • main.cpp: Demuestra la construcción por movimiento leyendo un ID de paquete y un tamaño de la entrada. Luego:
    • Crea un DataPacket llamado original con los valores de entrada
    • Imprime "Original - ID: <id>, Size: <size>, Sum: <sum>"
    • Crea un nuevo paquete llamado transferred moviendo desde original usando std::move()
    • Imprime "After move:"
    • Imprime "Original - ID: <id>, Size: <size>, Sum: <sum>"
    • Imprime "Transferred - ID: <id>, Size: <size>, Sum: <sum>"

El formato de entrada será:

  • Primera línea: ID del paquete (string)
  • Segunda línea: tamaño (entero)

Después del movimiento, el paquete original debe tener tamaño 0 y suma 0 (ya que sus datos fueron transferidos), mientras que el paquete transferido debe tener todos los datos originales. Esto demuestra cómo la semántica de movimiento evita copias profundas costosas simplemente transfiriendo la propiedad del puntero.

Incluye tu archivo de cabecera en main.cpp usando #include "DataPacket.h" y no olvides incluir <utility> para std::move().

Hoja de referencia

Un constructor de movimiento transfiere recursos de un objeto temporal en lugar de copiarlos, evitando copias profundas costosas. Toma una referencia rvalue (&&) y "roba" los recursos, dejando al objeto de origen en un estado válido pero vacío.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    // Constructor de movimiento
    Buffer(Buffer&& other) noexcept {
        data = other.data;       // Robar el puntero
        size = other.size;
        other.data = nullptr;    // Dejar el origen en un estado válido
        other.size = 0;
    }
    
    ~Buffer() { delete[] data; }
};

Los constructores de movimiento se llaman al inicializar desde objetos temporales o al usar std::move():

Buffer createBuffer() {
    return Buffer(1000);    // Se usa el constructor de movimiento (valor de retorno)
}

Buffer b1(1000);
Buffer b2(std::move(b1));   // Movimiento explícito - b1 ahora está vacío

Marque los constructores de movimiento como noexcept cuando sea posible para habilitar optimizaciones en los contenedores estándar.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include <utility>
#include "DataPacket.h"

using namespace std;

int main() {
    // Leer entrada
    string packetId;
    int size;
    cin >> packetId;
    cin >> size;

    // TODO: Crear un DataPacket llamado 'original' con los valores de entrada

    // TODO: Imprimir "Original - ID: <id>, Size: <size>, Sum: <sum>"

    // TODO: Crear un nuevo paquete llamado 'transferred' moviéndolo desde original
    // Hint: Usar std::move()

    // TODO: Imprimir "After move:"

    // TODO: Imprimir "Original - ID: <id>, Size: <size>, Sum: <sum>"

    // TODO: Imprimir "Transferred - ID: <id>, Size: <size>, Sum: <sum>"

    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos