Sobes.tech
Middle

Jak funguje čítač odkazů v std::shared_ptr?

sobes.tech AI

Odpověď od AI

Počítadlo odkazů ve std::shared_ptr je uloženo v samostatném objektu — v řídícím bloku (control block).

Tento řídící blok je vytvořen:

  • Při prvním vytvoření std::shared_ptr z ukazatele.
  • Při vytvoření std::shared_ptr s vlastním deleterem nebo alokátorem.
  • Při použití std::make_shared nebo std::allocate_shared.

Tento řídící blok obsahuje minimálně dva počítadla:

  1. Počítadlo silných odkazů (strong count): Zvyšuje se při vytváření nebo kopírování std::shared_ptr. Snižuje se při ničení std::shared_ptr. Když toto počítadlo dosáhne nuly, je spravovaný objekt uvolněn.
  2. Počítadlo slabých odkazů (weak count): Zvyšuje se při vytvoření std::weak_ptr z std::shared_ptr. Snižuje se při ničení std::weak_ptr. Řídící blok je uvolněn, když obě počítadla — silné i slabé — dosáhnou nuly.

Použití std::make_shared je preferováno před přímým vytvářením pomocí new, protože může alokovat objekt i řídící blok v jednom bloku paměti, což zlepšuje výkon a snižuje fragmentaci.

Počítadla jsou atomická, což činí std::shared_ptr bezpečným pro použití ve vícevláknových scénářích, ačkoliv přístup ke spravovanému objektu není ve výchozím nastavení synchronizován.

Příklad:

#include <iostream>
#include <memory>

struct MyObject {
    int data;
    MyObject(int d) : data(d) {
        std::cout << "MyObject(" << data << ") vytvořen\n";
    }
    ~MyObject() {
        std::cout << "MyObject(" << data << ") zničen\n";
    }
};

int main() {
    std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
    // silné počítadlo: 1, slabé počítadlo: 0

    {
        std::shared_ptr<MyObject> ptr2 = ptr1;
        // silné počítadlo: 2, slabé počítadlo: 0

        std::weak_ptr<MyObject> weak_ptr = ptr1;
        // silné počítadlo: 2, slabé počítadlo: 1

        if (auto locked_ptr = weak_ptr.lock()) {
            // silné počítadlo: 3, slabé počítadlo: 1 (dočasně v lock)
            std::cout << "Data objektu: " << locked_ptr->data << std::endl;
        }
        // silné počítadlo: 2, slabé počítadlo: 1
    } // ptr2 končí v scope
    // silné počítadlo: 1, slabé počítadlo: 1

    // Objekt ještě nebyl zničen, protože silné počítadlo == 1

    {
        std::weak_ptr<MyObject> weak_ptr2 = ptr1;
        // silné počítadlo: 1, slabé počítadlo: 2
    } // weak_ptr2 končí v scope
    // silné počítadlo: 1, slabé počítadlo: 1

} // ptr1 končí v scope
// silné počítadlo: 0, slabé počítadlo: 1
// Objekt je zničen, protože silné počítadlo dosáhlo nuly
// Řídící blok zůstává, dokud slabé počítadlo nedosáhne nuly