Sobes.tech
Middle

¿Qué sucede con los elementos de std::vector durante la operación realloc?

sobes.tech AI

Respuesta de la IA

std::vector no utiliza realloc. Cuando se cambia el tamaño del contenedor, requiriendo redistribución de memoria, sucede lo siguiente:

  1. Se asigna un nuevo bloque de memoria de tamaño suficiente.
  2. Los elementos del antiguo bloque se mueven al nuevo.
    • Para tipos que soportan semántica de movimiento (T con un constructor de movimiento y/o operador de asignación de movimiento correctamente implementados), se usa movimiento. Esto es más eficiente, ya que los recursos antiguos (por ejemplo, memoria asignada dentro de los objetos) se transfieren a los nuevos objetos, y los objetos antiguos quedan en un estado válido pero indefinido o se limpian con destructores.
    • Para tipos que no soportan semántica de movimiento o si el compilador no puede aplicar movimiento, se usa copia.
    • Si los elementos son triviales de copiar (T es un tipo POD o tiene operaciones triviales de copia y movimiento), se puede usar memcpy u otras funciones de bajo nivel para copiar datos sin procesar rápidamente.
  3. Se llaman destructores en los elementos del antiguo bloque de memoria.
  4. El antiguo bloque de memoria se libera.

Es importante notar que los punteros y referencias a los elementos en el antiguo bloque de memoria se vuelven inválidos después de la redistribución.

#include <vector>
#include <iostream>
#include <string>

struct MyItem {
    std::string name;
    // Constructor
    MyItem(const std::string& n) : name(n) {
        std::cout << "Constructing " << name << std::endl;
    }
    // Constructor de copia
    MyItem(const MyItem& other) : name(other.name) {
        std::cout << "Copying " << name << std::endl;
    }
    // Operador de asignación por copia
    MyItem& operator=(const MyItem& other) {
        if (this != &other) {
            name = other.name;
            std::cout << "Copy assigning " << name << std::endl;
        }
        return *this;
    }
    // Constructor de movimiento
    MyItem(MyItem&& other) noexcept : name(std::move(other.name)) {
        std::cout << "Moving " << name << std::endl;
    }
    // Operador de asignación por movimiento
    MyItem& operator=(MyItem&& other) noexcept {
        if (this != &other) {
            name = std::move(other.name);
            std::cout << "Move assigning " << name << std::endl;
        }
        return *this;
    }
    // Destructor
    ~MyItem() {
        std::cout << "Destroying " << name << std::endl;
    }
};

int main() {
    std::vector<MyItem> items;
    items.reserve(2); // Reservamos espacio para 2 elementos

    items.emplace_back("item1"); // Sin realloc
    items.emplace_back("item2"); // Sin realloc

    std::cout << "Adding third item, triggering realloc-like operation:" << std::endl;
    items.emplace_back("item3"); // Requiere realloc
    // La salida mostrará movimiento para item1 y item2, luego destrucción de los viejos objetos
    // y construcción del nuevo item3.
    // Si MyItem no soportara movimiento, se mostraría copia.

    return 0;
}

De esta forma, los elementos se mueven o copian a la nueva memoria, y luego se destruyen en la antigua.