Middle
Jak działa licznik referencji w std::shared_ptr?
sobes.tech AI
Odpowiedź od AI
Licznik referencji w std::shared_ptr jest przechowywany w osobnym obiekcie: w bloku kontrolnym (control block).
Blok kontrolny jest tworzony:
- Przy pierwszym utworzeniu
std::shared_ptrz wskaźnika. - Przy tworzeniu
std::shared_ptrz własnym deleterem lub alokatorami. - Przy użyciu
std::make_sharedlubstd::allocate_shared.
Ten blok kontrolny zawiera co najmniej dwa liczniki:
- Licznik silnych odniesień (strong count): Zwiększa się przy tworzeniu lub kopiowaniu
std::shared_ptr. Zmniejsza się przy niszczeniustd::shared_ptr. Gdy ten licznik osiągnie zero, zarządzany obiekt jest zwalniany. - Licznik słabych odniesień (weak count): Zwiększa się przy tworzeniu
std::weak_ptrzstd::shared_ptr. Zmniejsza się przy niszczeniustd::weak_ptr. Blok kontrolny jest zwalniany, gdy oba liczniki — silne i słabe — osiągną zero.
Użycie std::make_shared jest preferowane nad bezpośrednim tworzeniem z new, ponieważ może alokować obiekt i blok kontrolny w jednym bloku pamięci, co poprawia wydajność i zmniejsza fragmentację.
Liczniki są atomowe, co czyni std::shared_ptr bezpiecznym do użycia w scenariuszach wielowątkowych, choć dostęp do zarządzanego obiektu nie jest domyślnie synchronizowany.
Przykład:
#include <iostream>
#include <memory>
struct MyObject {
int data;
MyObject(int d) : data(d) {
std::cout << "MyObject(" << data << ") utworzony\n";
}
~MyObject() {
std::cout << "MyObject(" << data << ") zniszczony\n";
}
};
int main() {
std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
// licznik silny: 1, licznik słaby: 0
{
std::shared_ptr<MyObject> ptr2 = ptr1;
// licznik silny: 2, licznik słaby: 0
std::weak_ptr<MyObject> weak_ptr = ptr1;
// licznik silny: 2, licznik słaby: 1
if (auto locked_ptr = weak_ptr.lock()) {
// licznik silny: 3, licznik słaby: 1 (tymczasowo w lock)
std::cout << "Dane obiektu: " << locked_ptr->data << std::endl;
}
// licznik silny: 2, licznik słaby: 1
} // ptr2 wychodzi z zakresu
// licznik silny: 1, licznik słaby: 1
// Obiekt jeszcze nie został zniszczony, ponieważ licznik silny == 1
{
std::weak_ptr<MyObject> weak_ptr2 = ptr1;
// licznik silny: 1, licznik słaby: 2
} // weak_ptr2 wychodzi z zakresu
// licznik silny: 1, licznik słaby: 1
} // ptr1 wychodzi z zakresu
// licznik silny: 0, licznik słaby: 1
// Obiekt jest niszczony, ponieważ licznik silny osiągnął 0
// Blok kontrolny pozostaje do momentu, aż licznik słaby osiągnie 0