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:
- Se asigna un nuevo bloque de memoria de tamaño suficiente.
- Los elementos del antiguo bloque se mueven al nuevo.
- Para tipos que soportan semántica de movimiento (
Tcon 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 (
Tes un tipo POD o tiene operaciones triviales de copia y movimiento), se puede usarmemcpyu otras funciones de bajo nivel para copiar datos sin procesar rápidamente.
- Para tipos que soportan semántica de movimiento (
- Se llaman destructores en los elementos del antiguo bloque de memoria.
- 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.