Ok, więc ostatnim razem, gdy pisałem C ++ na życie, std::auto_ptr
było wszystko, co było dostępne w standardowej wersji lib, i to boost::shared_ptr
była wściekłość. Nigdy tak naprawdę nie przyglądałem się innym dostępnym rodzajom inteligentnych wskaźników. Rozumiem, że C ++ 11 zapewnia teraz niektóre typy ulepszeń, ale nie wszystkie.
Czy ktoś ma prosty algorytm, aby określić, kiedy użyć którego inteligentnego wskaźnika? Najlepiej zawierające porady dotyczące głupich wskaźników (surowe wskaźniki, takie jak T*
) i resztę inteligentnych wskaźników zwiększających. (Coś jak to byłoby świetnie).
Odpowiedzi:
Współwłasność: a średnia przyjmowane są prawie takie same jak ich odpowiedniki Google Boost . Używaj ich, gdy chcesz udostępnić zasób i nie wiesz, który z nich będzie ostatnim, który przeżyje. Służy do obserwowania udostępnionego zasobu bez wpływu na jego żywotność, aby nie przerywać cykli. Cykle z nie powinny normalnie się zdarzyć - dwa zasoby nie mogą się posiadać.
shared_ptr
weak_ptr
weak_ptr
shared_ptr
Pamiętaj, że Boost dodatkowo oferuje oferty
shared_array
, które mogą być odpowiednią alternatywą dlashared_ptr<std::vector<T> const>
.Następnie oferty Boost
intrusive_ptr
, które są lekkim rozwiązaniem, jeśli Twój zasób oferuje już zarządzanie liczone według referencji i chcesz dostosować je do zasady RAII. Ten nie został przyjęty przez standard.Unikalna własność:
Boost ma również
scoped_ptr
opcję, której nie można skopiować i dla której nie można określić kasatora.std::unique_ptr
jestboost::scoped_ptr
na sterydach i powinien być domyślnym wyborem, gdy potrzebujesz inteligentnego wskaźnika . Pozwala określić separator w argumentach szablonu i jest ruchomy , w przeciwieństwie doboost::scoped_ptr
. Jest również w pełni użyteczny w kontenerach STL, o ile nie używasz operacji wymagających typów do kopiowania (oczywiście).Zauważ jeszcze raz, że Boost ma wersję tablicową:
scoped_array
którą zunifikował standard, wymagającstd::unique_ptr<T[]>
częściowej specjalizacji, która będziedelete[]
wskaźnikiem zamiastdelete
go (za pomocądefault_delete
r).std::unique_ptr<T[]>
oferuje równieżoperator[]
zamiastoperator*
ioperator->
.Pamiętaj, że
std::auto_ptr
nadal jest w standardzie, ale jest przestarzały .§D.10 [depr.auto.ptr]
Brak prawa własności:
używaj głupich wskaźników (wskaźników surowych) lub odniesień do nieposiadających własności odniesień do zasobów i gdy wiesz, że zasób przeżyje obiekt / zakres odniesienia. Preferuj odniesienia i używaj surowych wskaźników, gdy potrzebujesz zerowalności lub możliwości resetowania.
Jeśli chcesz odwołać się do zasobu, który nie jest właścicielem, ale nie wiesz, czy zasób przeżyje obiekt, który się do niego odwołuje, spakuj go
shared_ptr
i użyjweak_ptr
- możesz przetestować, czy rodzicshared_ptr
żyjelock
, co spowoduje zwraca wartość innąshared_ptr
niż null, jeśli zasób nadal istnieje. Jeśli chcesz sprawdzić, czy zasób jest martwy, użyjexpired
. Oba mogą brzmieć podobnie, ale są bardzo różne w obliczu równoczesnego wykonywania, ponieważexpired
gwarantuje tylko wartość zwracaną dla tej pojedynczej instrukcji. Pozornie niewinny testjest potencjalnym warunkiem wyścigu.
źródło
shared_ptr
a nie posiadanie wskaźnika na aweak_ptr
...shared_array<T>
jest alternatywą dlashared_ptr<T[]>
nieshared_ptr<vector<T>>
: nie może rosnąć.Decyzja o wyborze inteligentnego wskaźnika jest kwestią własności . Jeśli chodzi o zarządzanie zasobami, obiekt A jest właścicielem obiektu B, jeśli kontroluje on czas życia obiektu B. Na przykład zmienne składowe są własnością ich odpowiednich obiektów, ponieważ czas życia zmiennych składowych jest powiązany z czasem życia obiektu. Wybierz inteligentne wskaźniki na podstawie tego, jak obiekt jest własnością.
Pamiętaj, że własność w systemie oprogramowania jest oddzielna od własności, tak jakbyśmy myśleli o tym poza oprogramowaniem. Na przykład osoba może „posiadać” swój dom, ale to niekoniecznie oznacza, że
Person
obiekt ma kontrolę nad czasem życiaHouse
obiektu. Połączenie tych rzeczywistych koncepcji z koncepcjami oprogramowania to niezawodny sposób na zaprogramowanie się w dziurze.Jeśli masz wyłączną własność obiektu, użyj
std::unique_ptr<T>
.Jeśli masz wspólną własność obiektu ...
- Jeśli nie ma cykli własności, użyj
std::shared_ptr<T>
.- Jeśli występują cykle, zdefiniuj „kierunek” i używaj
std::shared_ptr<T>
w jednym kierunku istd::weak_ptr<T>
w drugim.Jeśli obiekt należy do Ciebie, ale istnieje prawdopodobieństwo, że nie masz właściciela, użyj normalnych wskaźników
T*
(np. Wskaźników nadrzędnych).Jeśli obiekt jest Twoją własnością (lub w inny sposób gwarantuje istnienie), użyj referencji
T&
.Uwaga: należy pamiętać o kosztach inteligentnych wskaźników. W środowiskach o ograniczonej pamięci lub wydajności korzystne może być użycie zwykłych wskaźników z bardziej ręcznym schematem zarządzania pamięcią.
Koszty:
std::shared_ptr
ma narzut przyrostu liczby referencyjnej kopii, plus zmniejszenie zniszczenia, po którym następuje sprawdzenie 0-krotności z usunięciem trzymanego obiektu. W zależności od implementacji może to nadmuchać kod i spowodować problemy z wydajnością.Przykłady:
Drzewo binarne nie jest właścicielem swojego rodzica, ale istnienie drzewa implikuje istnienie jego rodzica (lub
nullptr
dla katalogu głównego), więc używa normalnego wskaźnika. Drzewo binarne (z semantyką wartości) ma wyłączną własność swoich dzieci, więc tak jeststd::unique_ptr
.Tutaj węzeł listy posiada swoje następne i poprzednie listy, więc określamy kierunek i używamy
shared_ptr
do następnego iweak_ptr
poprzedniego, aby przerwać cykl.źródło
shared_ptr<BinaryTree>
dla dzieci iweak_ptr<BinaryTree>
dla relacji rodziców.Używaj przez
unique_ptr<T>
cały czas, z wyjątkiem sytuacji, gdy potrzebujesz zliczania referencji, w takim przypadku używajshared_ptr<T>
(i w bardzo rzadkich przypadkach,weak_ptr<T>
aby zapobiec cyklom referencyjnym). W prawie każdym przypadku przeniesienie unikalnej własności jest w porządku.Nieprzetworzone wskaźniki: Dobre tylko wtedy, gdy potrzebujesz zwrotów kowariantnych, co może się nie zdarzyć. W przeciwnym razie nie są strasznie przydatne.
Wskaźniki tablic:
unique_ptr
ma specjalizację, dlaT[]
której automatycznie wywołujedelete[]
wynik, więc możeszunique_ptr<int[]> p(new int[42]);
na przykład bezpiecznie to zrobić .shared_ptr
nadal potrzebujesz niestandardowego usuwacza, ale nie potrzebujesz specjalistycznego wspólnego lub unikalnego wskaźnika tablicy. Oczywiście takie rzeczy i tak najlepiej najlepiej zastąpićstd::vector
. Niestetyshared_ptr
nie zapewnia funkcji dostępu do tablicy, więc nadal będziesz musiał ręcznie wywoływaćget()
, aleunique_ptr<T[]>
zapewniaoperator[]
zamiastoperator*
ioperator->
. W każdym razie musisz sam sprawdzić granice. To sprawia, że jestshared_ptr
nieco mniej przyjazny dla użytkownika, choć zapewne ogólna przewaga i brak zależności od wzmocnienia,unique_ptr
ashared_ptr
zwycięzcy ponownie.Wskaźniki o zasięgu: Nieistotne
unique_ptr
, podobnie jakauto_ptr
.Naprawdę nie ma w tym nic więcej. W C ++ 03 bez semantyki ruchu sytuacja ta była bardzo skomplikowana, ale w C ++ 11 rada jest bardzo prosta.
Nadal istnieją zastosowania innych inteligentnych wskaźników, takich jak
intrusive_ptr
lubinterprocess_ptr
. Są jednak bardzo niszowe i całkowicie niepotrzebne w ogólnym przypadku.źródło
std::unique_ptr<T[]>
zapewniaoperator[]
zamiastoperator*
ioperator->
. Prawdą jest jednak, że nadal musisz sprawdzić się samodzielnie.Przypadki użycia
unique_ptr
:Przypadki użycia
shared_ptr
:Przypadki użycia
weak_ptr
:Możesz edytować i dodawać więcej
źródło