Jak działa słaby PST?

Rozumiem, jak używać weak_ptr i shared_ptr. Rozumiem, jak działa shared_ptr, licząc liczbę odniesień w jego obiekcie. Jak działa weak_ptr? Próbowałem odczytać kod źródłowy boost i nie jestem wystarczająco zaznajomiony z boost, aby zrozumieć wszystkie rzeczy, których używa.

Dzięki.
Author: Saurav Sahu, 2011-04-15

2 answers

shared_ptr używa dodatkowego obiektu" counter " (aka. "shared count "lub" control block"), aby zapisać liczbę referencji. (BTW: ten obiekt "counter" przechowuje również deleter.)

Każdy shared_ptr i weak_ptr zawiera wskaźnik do rzeczywistego punktu, a drugi wskaźnik do obiektu "counter".

Aby zaimplementować weak_ptr, obiekt "counter" przechowuje dwa różne liczniki:

  • "use count" jest liczbą instancji shared_ptr wskazujących na obiekt.
  • "słaby count "to liczba wystąpień weak_ptr wskazujących na obiekt, plus jeden, jeśli "użyj count" jest nadal > 0.

Punkt jest usuwany, gdy "liczba użycia" osiągnie zero.

Obiekt pomocniczy "counter" jest usuwany, gdy "słaba liczba" osiągnie zero (co oznacza, że "użyj liczby" również musi być zero, patrz powyżej).

Kiedy próbujesz uzyskać shared_ptr z weak_ptr, biblioteka atomicznie sprawdza "liczbę użycia", a jeśli jest > 0, to ją zwiększa. Jeśli to się powiedzie, dostaniesz swój shared_ptr. Jeśli "use count" było już zerem, to zamiast tego otrzymujemy pustą instancję shared_ptr.


EDIT: Dlaczego dodają jeden do słabego liczenia, zamiast po prostu zwalniać obiekt "counter", gdy oba liczenia spadną do zera? Dobre pytanie.

Alternatywą byłoby usunięcie obiektu "counter", gdy zarówno "use count", jak i" weak count " spadną do zera. Oto pierwszy powód: sprawdzanie dwóch (wielkości wskaźnika) liczników atomicznie nie jest możliwe na każdej platformie, i nawet tam, gdzie jest, jest to bardziej skomplikowane niż sprawdzanie tylko jednego licznika.

Innym powodem jest to, że deleter musi pozostać ważny, dopóki nie zakończy wykonywania. Ponieważ deleter jest przechowywany w obiekcie "counter", to znaczy, że obiekt "counter" musi pozostać ważny. Zastanów się, co może się stać, jeśli istnieje jeden shared_ptr i jeden weak_ptr do jakiegoś obiektu, a zostaną one resetowane w tym samym czasie w równoległych wątkach. Powiedzmy, że shared_ptr jest na pierwszym miejscu. Zmniejsza "liczbę użycia" do zera i zaczyna wykonuję deleter. Teraz weak_ptr zmniejsza "słabą liczbę" do zera, a "użyj liczby" również wynosi zero. Usuwa więc obiekt "licznik", a wraz z nim deleter. Podczas gdy deleter nadal działa.

Oczywiście istnieją różne sposoby zapewnienia, że obiekt "licznik" pozostanie żywy, ale myślę, że zwiększenie "słabego licznika" o jeden jest bardzo eleganckim i intuicyjnym rozwiązaniem. "Słaby licznik" staje się licznikiem odniesienia dla obiektu "licznik". I od shared_ptr s odwołaj się do obiektu licznika, oni też muszą zwiększyć "słabą liczbę".

Prawdopodobnie jeszcze bardziej intuicyjnym rozwiązaniem byłoby zwiększenie " słabej liczby "dla każdego pojedynczego shared_ptr, ponieważ każdy pojedynczy shared_ptr hold jest odniesieniem do obiektu" counter".

Dodanie jednego dla wszystkich instancji shared_ptr jest tylko optymalizacją (zapisuje jeden atomic increment / decrement podczas kopiowania / przypisywania instancji shared_ptr).

 88
Author: Paul Groke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-08 16:10:23

Zasadniczo, "weak_ptr" jest zwykłym wskaźnikiem " T*", który pozwala odzyskać silne odniesienie, np." shared_ptr", później w kodzie.

Podobnie jak zwykły T*, weak_ptr nie wykonuje żadnego zliczania referencji. Wewnętrznie, aby wspierać referencję-licząc na dowolny typ T, STL (lub inna biblioteka implementująca tego rodzaju logikę) tworzy obiekt wrappera, który nazwiemy "Anchor". "Anchor" istnieje wyłącznie po to, aby zaimplementować licznik referencji i " gdy licznik jest zerowy, wywołaj delete" zachowanie, którego potrzebujemy.

W silnej referencji, shared_ptr implementuje swój copy, operator=, konstruktor, Destruktor i inne istotne API, aby zaktualizować liczbę referencji "Anchor". W ten sposób shared_ptr zapewnia, że twoje "T" żyje dokładnie tak długo, jak ktoś go używa. W "weak_ptr"te same API po prostu kopiują rzeczywistą kotwicę ptr. Nie aktualizują liczników referencyjnych.

Dlatego najważniejsze API "weak_ptr" to "expired" i źle nazwane "lock". "Expired" mówi, czy bazowy obiekt jest nadal w pobliżu - tzn. " czy już się usunął, ponieważ wszystkie silne odniesienia wyszły poza zakres?". "Lock" (jeśli potrafi) przekonwertuje weak_ptr na silne reference shared_ptr, przywracając liczenie referencji.

BTW, "lock" to straszna nazwa dla tego API. Nie powołujesz się (tylko) na mutex, tworzysz mocne odniesienie ze słabego, z tą" kotwicą " działającą. Największą wadą obu szablonów jest to, że nie zaimplementowały operator ->, więc aby zrobić cokolwiek ze swoim obiektem musisz odzyskać surowe " T*". Zrobili to głównie po to, aby wspierać takie rzeczy jak "shared_ptr", ponieważ prymitywne typy nie obsługują operatora" ->".

 -6
Author: user3726672,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-31 18:59:49