Sobes.tech
Middle

Hogyan működik a hivatkozásszámláló a std::shared_ptr-ben?

sobes.tech MI

Válasz az MI-től

A std::shared_ptr hivatkozásszámlálója egy külön objektumban, az ellenőrző blokkban (control block) tárolódik.

Ez az ellenőrző blokk akkor jön létre:

  • Amikor először hozunk létre std::shared_ptr-t mutatóval.
  • Amikor std::shared_ptr-t hozunk létre egyedi törlővel vagy allokátorral.
  • A std::make_shared vagy std::allocate_shared használatakor.

Ez az ellenőrző blokk legalább két számlálót tartalmaz:

  1. Erős hivatkozásszámláló (strong count): Növekszik, amikor std::shared_ptr-t hozunk létre vagy másolunk. Csökken, amikor std::shared_ptr-t törlünk. Ha ez a számláló nullára csökken, az irányított objektum felszabadul.
  2. Gyenge hivatkozásszámláló (weak count): Növekszik, amikor std::weak_ptr-t hozunk létre std::shared_ptr-ból. Csökken, amikor std::weak_ptr-t törlünk. Az ellenőrző blokk akkor szabadul fel, amikor mindkét számláló — erős és gyenge — nullára csökken.

A std::make_shared használata előnyösebb, mint a közvetlen new-elés, mert az objektumot és az ellenőrző blokkot egyetlen memória blokkban tudja lefoglalni, ez javítja a teljesítményt és csökkenti a fragmentációt.

A számlálók atomikusak, így a std::shared_ptr biztonságos több szálon való használatra, bár az irányított objektumhoz való hozzáférés alapértelmezés szerint nem szinkronizált.

Példa:

#include <iostream>
#include <memory>

struct MyObject {
    int data;
    MyObject(int d) : data(d) {
        std::cout << "MyObject(" << data << ") létrehozva\n";
    }
    ~MyObject() {
        std::cout << "MyObject(" << data << ") megsemmisítve\n";
    }
};

int main() {
    std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
    // erős számláló: 1, gyenge számláló: 0

    {
        std::shared_ptr<MyObject> ptr2 = ptr1;
        // erős számláló: 2, gyenge számláló: 0

        std::weak_ptr<MyObject> weak_ptr = ptr1;
        // erős számláló: 2, gyenge számláló: 1

        if (auto locked_ptr = weak_ptr.lock()) {
            // erős számláló: 3, gyenge számláló: 1 (ideiglenes lock alatt)
            std::cout << "Objektum adatai: " << locked_ptr->data << std::endl;
        }
        // erős számláló: 2, gyenge számláló: 1
    } // ptr2 kilép a scope-ból
    // erős számláló: 1, gyenge számláló: 1

    // Az objektum még nem lett megsemmisítve, mert az erős számláló == 1

    {
        std::weak_ptr<MyObject> weak_ptr2 = ptr1;
        // erős számláló: 1, gyenge számláló: 2
    } // weak_ptr2 kilép a scope-ból
    // erős számláló: 1, gyenge számláló: 1

} // ptr1 kilép a scope-ból
// erős számláló: 0, gyenge számláló: 1
// Az objektum megsemmisül, mert az erős számláló elérte a 0-t
// Az ellenőrző blokk akkor szabadul fel, amikor a gyenge számláló is 0 lesz