Jakiego rodzaju wskaźnika użyć kiedy?
Ok, więc ostatnim razem, kiedy pisałem C++, std::auto_ptr
było wszystko, co było dostępne, i boost::shared_ptr
było wściekłością. Nigdy tak naprawdę nie przyjrzałem się innym inteligentnym typom wskaźników. Rozumiem, że C++11 udostępnia teraz niektóre z typów, ale nie wszystkie.
Czy ktoś ma prosty algorytm do określenia, kiedy użyć inteligentnego wskaźnika? Najlepiej zawierać porady dotyczące durnych wskaźników (raw pointers jak T*
) i resztę boost Inteligentne Wskaźniki. (Coś w rodzaju to byłoby świetne).
4 answers
Współwłasność: shared_ptr
i weak_ptr
przyjęty standard są prawie takie same jak ich odpowiedniki wzmocnienia . Używaj ich, gdy chcesz udostępnić zasób i nie wiesz, który z nich będzie ostatni. Użyj weak_ptr
, aby obserwować udostępniony zasób bez wpływu na jego żywotność, a nie przerywać cykle. Cykle z shared_ptr
nie powinny się normalnie zdarzyć - dwa zasoby nie mogą się posiadać.
Zauważ, że Boost dodatkowo oferuje shared_array
, co może być odpowiednią alternatywą dla shared_ptr<std::vector<T> const>
.
Następny, Boost oferty intrusive_ptr
, które są lekkim rozwiązaniem, jeśli Twój zasób oferuje już zarządzanie zliczane odniesienia i chcesz go zaadoptować do zasady RAII. Ten nie został przyjęty przez normę.
Unikalne własności:
Boost ma również scoped_ptr
, który nie jest kopiowalny i dla którego nie można określić deleter. std::unique_ptr
jest boost::scoped_ptr
na sterydach i powinien być twoim domyślnym wybór, gdy potrzebujesz inteligentnego wskaźnika . Pozwala określić deleter w argumentach szablonu i jest ruchomy, W przeciwieństwie do boost::scoped_ptr
. Jest również w pełni użyteczny w kontenerach STL, o ile nie używasz operacji, które wymagają typów kopiowalnych (oczywiście).
Zauważ jeszcze raz, że Boost ma wersję tablicy: scoped_array
, które standard zunifikował wymagając std::unique_ptr<T[]>
częściowej specjalizacji, która będzie delete[]
wskaźnikiem zamiast delete
ING it (z default_delete
r). std::unique_ptr<T[]>
także oferty operator[]
zamiast operator*
i operator->
.
Zauważ, że std::auto_ptr
jest nadal w standardzie, ale jest przestarzały .
§D.10 [depr.auto.ptr]
Szablon klasy {[23] } jest przestarzały. [Uwaga: szablon klasy
unique_ptr
(20.7.1) zapewnia lepsze rozwiązanie. - Uwaga końcowa ]
Brak własności:
Użyj dumb pointers (raw pointers) lub reference for non-owning reference to resources and when you know that the zasób przeżyje obiekt / zakres odniesienia. Preferuj referencje i używaj surowych wskaźników, gdy potrzebujesz albo nullability, albo przesiedlenia.
Jeśli chcesz mieć odniesienie nie będące własnością zasobu, ale nie wiesz, czy zasób przeżyje obiekt, który się do niego odwołuje, spakuj zasób do shared_ptr
i użyj weak_ptr
- możesz sprawdzić, czy rodzic shared_ptr
żyje z lock
, które zwróci shared_ptr
, które nie jest null, jeśli zasób nadal istnieje. Jeśli chcesz sprawdzić, czy zasób jest martwy, użyj expired
. Oba mogą brzmieć podobnie, ale są bardzo różne w obliczu równoległego wykonania, ponieważ expired
gwarantuje tylko jego wartość zwracaną dla tego pojedynczego polecenia. Z pozoru niewinny test Jak
if(!wptr.expired())
something_assuming_the_resource_is_still_alive();
To potencjalny stan rasy.
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-10-27 08:48:19
Decydowanie, jakiego inteligentnego wskaźnika użyć, jest kwestią własności. Jeśli chodzi o zarządzanie zasobami, obiekt a jest właścicielem obiektu B, jeśli kontroluje czas życia obiektu B. na przykład zmienne członkowskie są własnością ich odpowiednich obiektów, ponieważ czas życia zmiennych członkowskich jest związany z Czasem Życia obiektu. Inteligentne Wskaźniki wybiera się na podstawie tego, jak obiekt jest własnością.
Zauważ, że własność w systemie oprogramowania jest odrębna od własności jako myślimy o tym poza oprogramowaniem. Na przykład osoba może "posiadać" swój dom, ale nie musi to oznaczać, że obiekt Person
ma kontrolę nad czasem życia obiektu House
. Połączenie tych rzeczywistych pojęć z pojęciami oprogramowania jest pewnym sposobem zaprogramowania się w dziurę.
Jeśli posiadasz wyłączną własność obiektu, użyj std::unique_ptr<T>
.
Jeśli posiadasz współwłasność obiektu...
- Jeśli nie ma cykli w posiadaniu, użyj std::shared_ptr<T>
.
- Jeśli są cykle, zdefiniuj "kierunek" i użyj std::shared_ptr<T>
w jednym kierunku i std::weak_ptr<T>
w drugim.
Jeśli obiekt jest twoją własnością, ale istnieje możliwość braku właściciela, użyj zwykłych wskaźników T*
(np. wskaźników nadrzędnych).
Jeśli obiekt jest twoją własnością (lub w inny sposób ma zagwarantowane 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 normalnego wskaźniki z bardziej ręcznym schematem zarządzania pamięcią.
Koszty:
- Jeśli masz niestandardowy deleter (np. używasz puli alokacji), spowoduje to obciążenie na wskaźnik, którego można łatwo uniknąć przez ręczne usunięcie.
-
std::shared_ptr
mA narzut przyrostu liczby referencji przy kopiowaniu, plus zmniejszenie przy niszczeniu, a następnie sprawdzenie zliczania 0 z usunięciem przechowywanego obiektu. W zależności od implementacji może to wydmuchać Twój kod i spowodować problemy z wydajnością. - czas kompilacji. Podobnie jak w przypadku wszystkich szablonów, Inteligentne Wskaźniki negatywnie wpływają na czas kompilacji.
Przykłady:
struct BinaryTree
{
Tree* m_parent;
std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};
Drzewo binarne nie posiada swojego rodzica, ale istnienie drzewa implikuje istnienie jego rodzica (lub nullptr
dla roota), więc używa zwykłego wskaźnika. Drzewo binarne (z semantyką wartości) ma wyłączną własność swoich dzieci, więc są to std::unique_ptr
.
struct ListNode
{
std::shared_ptr<ListNode> m_next;
std::weak_ptr<ListNode> m_prev;
};
Tutaj węzeł listy posiada swój następny i poprzedni listy, więc definiujemy kierunek i używamy shared_ptr
dla next i weak_ptr
dla prev, aby przerwać cykl.
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
2012-01-08 14:13:49
Używaj unique_ptr<T>
cały czas, z wyjątkiem sytuacji, gdy potrzebujesz zliczania referencji, w którym to przypadku użyj shared_ptr<T>
(a w bardzo rzadkich przypadkach, weak_ptr<T>
, aby zapobiec cyklom referencyjnym). W prawie każdym przypadku przenoszenie unikalnej własności jest w porządku.
Raw pointers: dobre tylko wtedy, gdy potrzebujesz kowariantnych zwrotów, które mogą się zdarzyć bez posiadania wskazań. Inaczej nie są zbyt przydatne.
Wskaźniki tablicy: unique_ptr
ma specjalizację dla T[]
, która automatycznie wywołuje delete[]
na wyniku, dzięki czemu można na przykład bezpiecznie wykonaj unique_ptr<int[]> p(new int[42]);
. shared_ptr
nadal potrzebujesz niestandardowego deletera, ale nie potrzebujesz specjalistycznego współdzielonego lub unikalnego wskaźnika tablicy. Oczywiście takie rzeczy zazwyczaj najlepiej zastąpić std::vector
. Niestety shared_ptr
nie zapewnia funkcji dostępu do tablicy, więc nadal musisz ręcznie wywołać get()
, ale unique_ptr<T[]>
dostarcza operator[]
zamiast operator*
i operator->
. W każdym razie musisz sprawdzić sam. To sprawia, że shared_ptr
jest nieco mniej przyjazny dla użytkownika, chociaż prawdopodobnie ogólny przewaga i brak zależności Boost czyni unique_ptr
i shared_ptr
ponownie zwycięzcami.
Wskaźniki zakresowe: nieistotne przez unique_ptr
, tak jak auto_ptr
.
Nadal istnieją zastosowania dla innych inteligentnych wskaźników, takich jak intrusive_ptr
lub interprocess_ptr
. Jednak są one bardzo niszowe i całkowicie niepotrzebne w ogólnym przypadku.
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
2012-01-02 23:35:01
Przypadki kiedy używać unique_ptr
:
- metody fabryczne
- Członkowie, którzy są wskaźnikami (w tym pimpl)
- Przechowywanie wskaźników w kontenerach stl (aby uniknąć ruchów)
- użycie dużych lokalnych obiektów dynamicznych
Przypadki kiedy używać shared_ptr
:
- współdzielenie obiektów między wątkami
- dzielenie obiektów w ogóle
Przypadki kiedy używać weak_ptr
:
- Duża mapa, która działa jako ogólne odniesienie (ex. mapa wszystkich otwartych gniazda)
Zapraszam do edycji i dodawania kolejnych
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
2018-09-16 00:19:46