Sobes.tech
Middle

Hoe werkt de referentieteller in std::shared_ptr?

sobes.tech AI

Antwoord van AI

De referentieteller in std::shared_ptr wordt opgeslagen in een apart object: de control block.

Het control block wordt aangemaakt:

  • Bij de eerste creatie van een std::shared_ptr uit een pointer.
  • Bij het maken van een std::shared_ptr met een aangepaste deleter of allocator.
  • Bij gebruik van std::make_shared of std::allocate_shared.

Dit control block bevat ten minste twee tellers:

  1. Sterke referentieteller (strong count): Wordt verhoogd bij het maken of kopiëren van een std::shared_ptr. Wordt verlaagd bij het vernietigen van een std::shared_ptr. Wanneer deze teller nul bereikt, wordt het beheerde object vrijgegeven.
  2. Zwakke referentieteller (weak count): Wordt verhoogd bij het maken van een std::weak_ptr uit een std::shared_ptr. Wordt verlaagd bij het vernietigen van een std::weak_ptr. Het control block wordt vrijgegeven wanneer beide tellers — sterk en zwak — nul worden.

Het gebruik van std::make_shared heeft de voorkeur boven directe creatie met new, omdat het het object en het control block in één geheugenblok kan alloceren, wat de prestaties verbetert en fragmentatie vermindert.

De tellers zijn atomair, wat std::shared_ptr veilig maakt voor gebruik in multithread scenario's, hoewel toegang tot het beheerde object standaard niet gesynchroniseerd is.

Voorbeeld:

#include <iostream>
#include <memory>

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

int main() {
    std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
    // sterke teller: 1, zwakke teller: 0

    {
        std::shared_ptr<MyObject> ptr2 = ptr1;
        // sterke teller: 2, zwakke teller: 0

        std::weak_ptr<MyObject> weak_ptr = ptr1;
        // sterke teller: 2, zwakke teller: 1

        if (auto locked_ptr = weak_ptr.lock()) {
            // sterke teller: 3, zwakke teller: 1 (tijdelijk in lock)
            std::cout << "Object data: " << locked_ptr->data << std::endl;
        }
        // sterke teller: 2, zwakke teller: 1
    } // ptr2 gaat uit scope
    // sterke teller: 1, zwakke teller: 1

    // Het object is nog niet vernietigd, omdat sterke teller == 1

    {
        std::weak_ptr<MyObject> weak_ptr2 = ptr1;
        // sterke teller: 1, zwakke teller: 2
    } // weak_ptr2 gaat uit scope
    // sterke teller: 1, zwakke teller: 1

} // ptr1 gaat uit scope
// sterke teller: 0, zwakke teller: 1
// Het object wordt vernietigd, omdat sterke teller 0 is
// Het control block blijft totdat zwakke teller 0 wordt