Sobes.tech
Middle

Что происходит с элементами std::vector при выполнении операции realloc?

sobes.tech AI

Answer from AI

std::vector does not use realloc. When resizing the container, requiring memory reallocation, the following occurs:

  1. A new memory region of sufficient size is allocated.
  2. Elements from the old memory region are moved to the new one.
    • For types supporting move semantics (T with a proper move constructor and/or move assignment operator), move semantics are used. This is more efficient as old resources (e.g., memory allocated within objects) are transferred to new objects, and old objects are left in a valid but unspecified state or are destroyed.
    • For types that do not support move semantics or if the compiler cannot apply move semantics, copying is used.
    • If elements are trivially copyable (T is a POD type or has trivial copy and move operations), memcpy or similar low-level functions may be used for fast copying of raw data.
  3. Destructors are called for elements in the old memory region.
  4. The old memory region is freed.

It is important to note that pointers and references to elements in the old memory region become invalid after reallocation.

#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;
    }
    // Copy constructor
    MyItem(const MyItem& other) : name(other.name) {
        std::cout << "Copying " << name << std::endl;
    }
    // Copy assignment operator
    MyItem& operator=(const MyItem& other) {
        if (this != &other) {
            name = other.name;
            std::cout << "Copy assigning " << name << std::endl;
        }
        return *this;
    }
    // Move constructor
    MyItem(MyItem&& other) noexcept : name(std::move(other.name)) {
        std::cout << "Moving " << name << std::endl;
    }
    // Move assignment operator
    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); // Reserve space for 2 elements

    items.emplace_back("item1"); // No reallocation
    items.emplace_back("item2"); // No reallocation

    std::cout << "Adding third item, triggering realloc-like operation:" << std::endl;
    items.emplace_back("item3"); // Reallocation required
    // The output will show move operations for item1 and item2, then destruction of old objects
    // and construction of the new item3.
    // If MyItem did not support move semantics, copying would be used instead.

    return 0;
}

Thus, elements are moved or copied to the new memory region, then destroyed in the old one.