Odnotowano szereg pytań dotyczących std::weak_ptr
dzisiaj i std::owner_less
i ich zastosowanie w pojemnikach asocjacyjnych std::set
i std::map
. Istnieje pewna liczba postów stwierdzających, że używanie weak_ptr
w std::set
jest niepoprawne, ponieważ w przypadku wygaśnięcia słabego wskaźnika będzie to niezdefiniowane zachowanie. Czy to jest poprawne?Czy można bezpiecznie używać weak_ptr w std :: set lub klucz std :: map
Odpowiedz
Jednym z powodów, dla których istnieje std::owner_less
jest dostarczenie tego zamówienia i zagwarantowanie jego bezpieczeństwa w obecności wygasających słabych wskaźników. My logika
pierwsze definicja std::owner_less
operatora() tworzy ścisłe słabą kolejność jak określono w 25,4
pod stosunku równoważnikowym określonej przez
operator()
,!operator()(a, b) && !operator()(b, a)
dwashared_ptr
lubweak_ptr
wystąpienia są równoważne wtedy i tylko wtedy, gdy mają wspólną własność lub są puste.
Oba przypadki są
- Dzielą ten sam przedmiot, co w praktyce oznacza, że dzielą ten sam obiekt licznika odwołań.
- Są puste.
Sądzę, że zamieszanie dotyczy drugiego okresu. Kluczem jest to, że "pusty" w standardzie oznacza, że weak_ptr
nie współdzieli własności z żadnym obiektem. Ponownie, standardowe stany
constexpr weak_ptr() noexcept;
Działanie: Tworzy pusty
weak_ptr
obiekt.
Postgrecje:use_count() == 0
.weak_ptr(const weak_ptr& r) noexcept;
template<class Y> weak_ptr(const weak_ptr<Y>& r) noexcept;
template<class Y> weak_ptr(const shared_ptr<Y>& r) noexcept;
Wymaga: Drugi i trzeci konstruktorzy nie bierze udziału w rozdzielczości przeciążenia chyba
Y*
jest niejawnie zamienny doT*
.Efekty: Jeśli
r
jest pusty, konstruuje pusty obiektweak_ptr
; w przeciwnym razie konstruuje obiektweak_ptr
, który dzieli własność zr
i przechowuje kopię wskaźnika przechowywaną wr
.Postgrecje:
use_count() == r.use_count()
.
Zmienne jest zdefiniowane jako zamiany stany obu weak_ptr
s, a przypisanie określono z zastosowaniem konstruktory powyżej wraz z wymiany.
klucz one zwrócić uwagę jest to, że jedynym sposobem, aby utworzyć pusty weak_ptr
jest domyślnie skonstruować go lub kopia/ruch/przypisać jedną z poprzednio pusty weak_ptr
lub shared_ptr
. Ważne jest również, aby pamiętać, że nie można uzyskać pustego weak_ptr
, po prostu wygasając weak_ptr
. Wygasły weak_ptr
ma po prostu zerowe zero.
W praktyce, gdy shared_ptr
tworzony jest obiekt liczba odniesienia mają być utworzone albo oddzielone od danych z wykorzystaniem konstruktora shared_ptr
lub w samej alokacji pamięci, gdy stosuje std::make_shared
. Kiedy weak_ptr
skonstruowany jest z tego shared_ptr
, wskaże tę samą strukturę kontrolną i liczbę odwołań. Po zniszczeniu kodu shared_ptr
może on zniszczyć dane, ale obiekt licznika odniesień musi pozostać, dopóki nie zostanie usunięty cały udział własności. W przeciwnym razie, weak_ptr
będzie mieć odniesienie wskaźnika zwisającego.
Tak, wszystko to razem wzięte oznacza, że jest bezpieczny w użyciu std::weak_ptr
jako klucz they z std::map
lub w std::set
, tak długo jak używasz std::owner_less
wykonać kolejność. Powyższe gwarantuje, że zamówienie weak_ptr
pozostanie niezmienione, nawet jeśli wygasa, gdy jest w kontenerze.
Czy możesz dodać wniosek do tej odpowiedzi? :) – Drax
@Drax: Jasne rzeczy. Napisałem to późno w nocy i straciłem trochę szczegółów. –
Piękny, dziękuję :) – Drax