Sobes.tech
Middle

Čo sa deje s prvkami std::vector pri operácii realloc?

sobes.tech AI

Odpoveď od AI

std::vector ne používa realloc. Pri zmene veľkosti kontajnera, ktorá vyžaduje prealokáciu pamäte, sa deje nasledovné:

  1. Vyhradí sa nová oblasť pamäte dostatočnej veľkosti.
  2. Prvky sa presunú zo starej oblasti do novej.
    • Pre typy, ktoré podporujú semantiku presunu (T s korektnou implementáciou konštruktora presunu a/alebo operátora priradenia presunom), sa použije presun. To je efektívnejšie, pretože staré zdroje (napríklad pamäť alokovaná vo vnútri objektov) sa prenášajú na nové objekty a staré objekty zostávajú v platnom, ale neurčitom stave alebo sa zničia destruktormi.
    • Pre typy, ktoré nepodporujú semantiku presunu alebo ak prekladač nemôže použiť presun, sa použije kopírovanie.
    • Ak sú prvky triviálne kopírovateľné (T je POD typ alebo má triviálne operácie kopírovania a presunu), môže sa použiť memcpy alebo podobné nízkoúrovňové funkcie na rýchle kopírovanie surových dát.
  3. Destruktory sa volajú pre prvky v starej oblasti pamäte.
  4. Stará oblasť pamäte sa uvoľní.

Je dôležité poznamenať, že ukazovatele a odkazy na prvky v starej oblasti pamäte sa po prealokovaní stanú neplatnými.

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

struct MyItem {
    std::string name;
    // Konštruktor
    MyItem(const std::string& n) : name(n) {
        std::cout << "Constructing " << name << std::endl;
    }
    // Konštruktor kopírovania
    MyItem(const MyItem& other) : name(other.name) {
        std::cout << "Copying " << name << std::endl;
    }
    // Operátor priradenia kopírovaním
    MyItem& operator=(const MyItem& other) {
        if (this != &other) {
            name = other.name;
            std::cout << "Copy assigning " << name << std::endl;
        }
        return *this;
    }
    // Konštruktor presunu
    MyItem(MyItem&& other) noexcept : name(std::move(other.name)) {
        std::cout << "Moving " << name << std::endl;
    }
    // Operátor priradenia presunom
    MyItem& operator=(MyItem&& other) noexcept {
        if (this != &other) {
            name = std::move(other.name);
            std::cout << "Move assigning " << name << std::endl;
        }
        return *this;
    }
    // Destruktor
    ~MyItem() {
        std::cout << "Destroying " << name << std::endl;
    }
};

int main() {
    std::vector<MyItem> items;
    items.reserve(2); // Rezervácia pamäte pre 2 prvky

    items.emplace_back("item1"); // Bez reallokácie
    items.emplace_back("item2"); // Bez reallokácie

    std::cout << "Adding third item, triggering realloc-like operation:" << std::endl;
    items.emplace_back("item3"); // Vyžaduje reallokáciu
    // Výstup ukáže presun pre item1 a item2, následne destrukciu starých objektov
    // a konštrukciu nového item3.
    // Ak by MyItem nepodporoval presun, bol by výstup o kopírovaní.

    return 0;
}