Sobes.tech
Middle

O que acontece com os elementos de std::vector durante a operação realloc?

sobes.tech IA

Resposta da IA

O std::vector não usa realloc. Quando se altera o tamanho do contêiner, exigindo uma redistribuição de memória, o seguinte acontece:

  1. Uma nova área de memória de tamanho suficiente é alocada.
  2. Os elementos da antiga área são movidos para a nova.
    • Para tipos que suportam semântica de movimento (T com um construtor de movimento e/ou operador de atribuição de movimento corretamente implementados), é usado o movimento. Isso é mais eficiente, pois os recursos antigos (por exemplo, memória alocada dentro dos objetos) são transferidos para os novos objetos, e os antigos permanecem em um estado válido, mas indefinido ou são limpos pelos destruidores.
    • Para tipos que não suportam semântica de movimento ou se o compilador não consegue aplicar o movimento, é usada cópia.
    • Se os elementos forem triviais de copiar (T é um tipo POD ou possui operações triviais de cópia e movimento), pode-se usar memcpy ou funções similares de baixo nível para copiar rapidamente os dados brutos.
  3. Os destruidores são chamados para os elementos na antiga área de memória.
  4. A antiga área de memória é liberada.

É importante notar que os ponteiros e referências aos elementos na antiga área tornam-se inválidos após a redistribuição.

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

struct MyItem {
    std::string name;
    // Construtor
    MyItem(const std::string& n) : name(n) {
        std::cout << "Constructing " << name << std::endl;
    }
    // Construtor de cópia
    MyItem(const MyItem& other) : name(other.name) {
        std::cout << "Copying " << name << std::endl;
    }
    // Operador de atribuição por cópia
    MyItem& operator=(const MyItem& other) {
        if (this != &other) {
            name = other.name;
            std::cout << "Copy assigning " << name << std::endl;
        }
        return *this;
    }
    // Construtor de movimento
    MyItem(MyItem&& other) noexcept : name(std::move(other.name)) {
        std::cout << "Moving " << name << std::endl;
    }
    // Operador de atribuição por movimento
    MyItem& operator=(MyItem&& other) noexcept {
        if (this != &other) {
            name = std::move(other.name);
            std::cout << "Move assigning " << name << std::endl;
        }
        return *this;
    }
    // Destruidor
    ~MyItem() {
        std::cout << "Destroying " << name << std::endl;
    }
};

int main() {
    std::vector<MyItem> items;
    items.reserve(2); // Reservar espaço para 2 elementos

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

    std::cout << "Adding third item, triggering realloc-like operation:" << std::endl;
    items.emplace_back("item3"); // Requer realloc
    // A saída mostrará o movimento para item1 e item2, depois a destruição dos objetos antigos
    // e a construção do novo item3.
    // Se MyItem não suportasse movimento, haveria uma cópia.

    return 0;
}