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).

Author: Community, 2012-01-03

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 deleteING it (z default_deleter). 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.

 171
Author: Xeo,
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.

 125
Author: Peter Alexander,
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.

Nie ma w tym nic więcej. W C++03 BEZ semantyki move sytuacja ta była bardzo skomplikowana, ale w C++11 porada jest bardzo prosta.

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.

 19
Author: Puppy,
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

 5
Author: Lalaland,
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