Sobes.tech
Middle

Cum funcționează contorul de referințe în std::shared_ptr?

sobes.tech AI

Răspuns de la AI

Contorul de referințe din std::shared_ptr este stocat într-un obiect separat: în blocul de control (control block).

Acest bloc de control este creat:

  • La prima creare a unui std::shared_ptr dintr-un pointer.
  • La crearea unui std::shared_ptr cu un deleter sau allocator personalizat.
  • La utilizarea std::make_shared sau std::allocate_shared.

Acest bloc de control conține cel puțin doi contori:

  1. Contorul de referințe puternice (strong count): Se mărește la crearea sau copierea unui std::shared_ptr. Se scade la distrugerea unui std::shared_ptr. Când acest contor ajunge la zero, obiectul gestionat este eliberat.
  2. Contorul de referințe slabe (weak count): Se mărește la crearea unui std::weak_ptr dintr-un std::shared_ptr. Se scade la distrugerea unui std::weak_ptr. Blocul de control este eliberat când ambele contoare — puternic și slab — ajung la zero.

Utilizarea std::make_shared este preferabilă față de crearea directă cu new, deoarece poate aloca obiectul și blocul de control într-un singur bloc de memorie, ceea ce îmbunătățește performanța și reduce fragmentarea.

Contorii sunt atomici, ceea ce face ca std::shared_ptr să fie sigur pentru utilizarea în scenarii multithread, deși accesul la obiectul gestionat nu este sincronizat implicit.

Exemplu:

#include <iostream>
#include <memory>

struct MyObject {
    int data;
    MyObject(int d) : data(d) {
        std::cout << "MyObject(" << data << ") creat\n";
    }
    ~MyObject() {
        std::cout << "MyObject(" << data << ") distrus\n";
    }
};

int main() {
    std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
    // contor puternic: 1, contor slab: 0

    {
        std::shared_ptr<MyObject> ptr2 = ptr1;
        // contor puternic: 2, contor slab: 0

        std::weak_ptr<MyObject> weak_ptr = ptr1;
        // contor puternic: 2, contor slab: 1

        if (auto locked_ptr = weak_ptr.lock()) {
            // contor puternic: 3, contor slab: 1 (temporar în lock)
            std::cout << "Date obiect: " << locked_ptr->data << std::endl;
        }
        // contor puternic: 2, contor slab: 1
    } // ptr2 iese din scope
    // contor puternic: 1, contor slab: 1

    // Obiectul nu a fost încă distrus, deoarece contorul puternic == 1

    {
        std::weak_ptr<MyObject> weak_ptr2 = ptr1;
        // contor puternic: 1, contor slab: 2
    } // weak_ptr2 iese din scope
    // contor puternic: 1, contor slab: 1

} // ptr1 iese din scope
// contor puternic: 0, contor slab: 1
// Obiectul este distrus, deoarece contorul puternic a ajuns la 0
// Blocul de control rămâne până când contorul slab ajunge la 0