Jakie są dostępne implementacje C++ Smart Pointer?

Porównania, Plusy, Minusy i kiedy używać?

Jest to spin-off z wątku garbage collection gdzie to, co myślałem, że jest prostą odpowiedzią, wygenerowało wiele komentarzy na temat niektórych konkretnych implementacji inteligentnych wskaźników, więc wydawało się, że warto zacząć nowy post.

Ostatecznie pytanie brzmi, Jakie są różne implementacje inteligentnych wskaźników w C++ i jak je porównać? Po prostu proste plusy i minusy lub wyjątki i gotchas do czegoś ty w przeciwnym razie powinno zadziałać.

Opublikowałem kilka implementacji, których użyłem lub przynajmniej przejrzałem i rozważałem użycie jako odpowiedź poniżej oraz moje zrozumienie ich różnic i podobieństw, które mogą nie być w 100% dokładne, więc nie krępuj się sprawdzać faktów lub poprawiać mnie w razie potrzeby.

Celem jest zapoznanie się z nowymi obiektami i bibliotekami lub poprawienie mojego wykorzystania i zrozumienia istniejących implementacji już powszechnie w użyciu i ostatecznie z porządnym odniesieniem do i inni

Author: Community, 2011-02-17

3 answers

C++03

std::auto_ptr - być może jeden z oryginałów, który cierpiał na syndrom pierwszego draftu, dostarczał tylko ograniczone urządzenia do zbierania śmieci. Pierwszym minusem jest to, że wywołuje delete po zniszczeniu, co czyni je niedopuszczalnymi do przechowywania obiektów przydzielonych tablicy (new[]). Przejmuje on własność wskaźnika, więc dwa automatyczne wskaźniki nie powinny zawierać tego samego obiektu. Przypisanie przeniesie własność i zresetuje rvalue auto wskaźnik NA null pointer. Co prowadzi do być może najgorszej wady; nie mogą być używane w kontenerach STL z powodu wspomnianej niemożności skopiowania. Ostatnim ciosem dla każdego przypadku użycia jest to, że mają być przestarzałe w następnym standardzie C++.

std::auto_ptr_ref - to nie jest inteligentny wskaźnik, to właściwie detal projektu używany w połączeniu z std::auto_ptr, aby umożliwić kopiowanie i przypisywanie w pewnych sytuacjach. W szczególności może być używany do konwersji non-const std::auto_ptr Na lvalue za pomocą Trik Colvina-Gibbonsa znany również jako konstruktor ruchu w celu przeniesienia własności.

Wręcz przeciwnie, być może std::auto_ptr nie był tak naprawdę przeznaczony do użycia jako inteligentny wskaźnik ogólnego przeznaczenia do automatycznego zbierania śmieci. Większość mojego ograniczonego zrozumienia i założeń opiera się na efektywnym wykorzystaniu auto_ptr Herba Suttera i używam go regularnie, choć nie zawsze w najbardziej zoptymalizowany sposób.


C++11

std::unique_ptr - to jest naszym przyjacielem, który zastąpi std::auto_ptr będzie to bardzo podobne z wyjątkiem kluczowych ulepszeń poprawiających słabe strony std::auto_ptr, takich jak praca z tablicami, lvalue Ochrona za pomocą prywatnego konstruktora kopiującego, możliwość użycia z kontenerami STL i algorytmami itp. Ponieważ narzut wydajności i pamięć są ograniczone, jest to idealny kandydat do zastąpienia, lub być może trafniej opisane jako posiadanie, surowych wskaźników. Jak to" unikalne " oznacza, że jest tylko jeden właściciel wskaźnik podobnie jak poprzedni std::auto_ptr.

std::shared_ptr - uważam, że jest to oparte na TR1 i boost::shared_ptr, ale poprawiono również aliasing i arytmetykę wskaźników. Krótko mówiąc, owija inteligentny wskaźnik referencyjny liczony wokół dynamicznie przydzielanego obiektu. Ponieważ "shared" oznacza, że wskaźnik może być własnością więcej niż jednego współdzielonego wskaźnika, gdy ostatnie odniesienie do ostatniego współdzielonego wskaźnika wyjdzie poza zakres, obiekt zostanie odpowiednio usunięty. Są one również bezpieczne dla wątku i może obsługiwać niekompletne typy w większości przypadków. std::make_shared może być użyty do efektywnego zbudowania std::shared_ptr z jedną alokacją sterty przy użyciu domyślnego alokatora.

std::weak_ptr - podobnie oparte na TR1 i boost::weak_ptr. Jest to odniesienie do obiektu należącego do std::shared_ptr i dlatego nie uniemożliwi usunięcia obiektu, jeśli liczba odniesień std::shared_ptr spadnie do zera. Aby uzyskać dostęp do surowego wskaźnika, musisz najpierw uzyskać dostęp do std::shared_ptr, wywołując lock, które zwróci empty std::shared_ptr Jeśli posiadany wskaźnik wygasł i został już zniszczony. Jest to przede wszystkim przydatne, aby uniknąć nieokreślonych wiszących zliczeń referencyjnych podczas korzystania z wielu inteligentnych wskaźników.


Boost

boost::shared_ptr - prawdopodobnie najłatwiejszy w użyciu w najbardziej zróżnicowanych scenariuszach (STL, PIMPL, RAII, itp.) Jest to inteligentny wskaźnik, do którego odwołuje się współdzielony. Słyszałem kilka skarg dotyczących wydajności i kosztów w niektórych sytuacjach, ale musiałem je zignorować bo nie pamiętam o co chodziło. Najwyraźniej był na tyle popularny, że stał się oczekującym standardowym obiektem C++ i nie przychodzą na myśl żadne wady w stosunku do normy dotyczące inteligentnych wskaźników.

boost::weak_ptr - podobnie jak poprzedni opis std::weak_ptr, oparty na tej implementacji, pozwala to na nie posiadanie odniesienia do boost::shared_ptr. Nic dziwnego, że wywołujesz lock(), Aby uzyskać dostęp do "silnego" współdzielonego wskaźnika i musisz sprawdzić, czy jest on ważny, ponieważ mógł już zostać zniszczony. Po prostu upewnij się, że nie przechowujesz zwróconego wskaźnika współdzielonego i pozwól mu wyjść poza zakres, gdy tylko skończysz z nim, w przeciwnym razie wrócisz do cyklicznego problemu odniesienia, w którym Twoje odniesienia będą zawieszone, a obiekty nie zostaną zniszczone.

boost::scoped_ptr - jest to prosta klasa inteligentnych wskaźników z niewielkim obciążeniem, prawdopodobnie zaprojektowana dla lepszej alternatywy dla boost::shared_ptr, gdy jest używana. Jest porównywalny do std::auto_ptr, zwłaszcza w tym, że nie można go bezpiecznie używać jako element kontenera STL lub z wieloma wskaźnikami do tego samego obiektu.

boost::intrusive_ptr - nigdy tego nie używałem, ale z mojego zrozumienia jest przeznaczony do użycia podczas tworzenia własnych klas kompatybilnych z inteligentnymi wskaźnikami. Musisz zaimplementować liczenie referencji samodzielnie, musisz również zaimplementować kilka metod, jeśli chcesz, aby Twoja klasa była ogólna, ponadto musisz zaimplementować własne bezpieczeństwo wątku. Na plus to prawdopodobnie daje najbardziej niestandardowy sposób wybierając i wybierając dokładnie, ile lub jak mało "smartness" chcesz. {[31] } jest zazwyczaj bardziej wydajny niż shared_ptr, ponieważ pozwala na przydzielenie jednej sterty na obiekt. (dzięki Arvid)

boost::shared_array - jest to boost::shared_ptr dla tablic. Zasadniczo new [], operator[], i oczywiście delete [] są pieczone w. To może być używane w kontenerach STL i z tego co wiem robi wszystko boost:shared_ptr, chociaż nie można używać boost::weak_ptr z tymi. Można jednak alternatywnie użyj boost::shared_ptr<std::vector<>> dla podobnej funkcjonalności i odzyskaj możliwość używania boost::weak_ptr dla referencji.

boost::scoped_array - jest to boost::scoped_ptr dla tablic. Podobnie jak w przypadku boost::shared_array wszystkie niezbędne wartości macierzy są wypiekane. Ten nie nadaje się do kopiowania i nie może być używany w kontenerach STL. Znalazłem prawie wszędzie, gdzie chcesz tego użyć, prawdopodobnie możesz po prostu użyć std::vector. Nigdy nie ustaliłem, który jest w rzeczywistości szybszy lub ma mniej napowietrznych, ale ten zasięg wydaje się znacznie mniejszy zaangażowany niż wektor STL. Jeśli chcesz zachować alokację na stosie, rozważ boost::array.


Qt

QPointer - wprowadzony w Qt 4.0 jest to" słaby " inteligentny wskaźnik, który działa tylko z QObject i pochodnymi klasami, które w Qt framework jest prawie wszystko, więc nie jest to tak naprawdę ograniczenie. Istnieją jednak ograniczenia, a mianowicie, że nie dostarcza "silnego" wskaźnika i chociaż można sprawdzić, czy podstawowy obiekt jest ważne z isNull() możesz znaleźć obiekt niszczony zaraz po przejściu tej kontroli, szczególnie w środowiskach wielowątkowych. Ludzie z Qt uważają to za przestarzałe.

QSharedDataPointer - jest to" silny " inteligentny wskaźnik potencjalnie porównywalny do boost::intrusive_ptr, chociaż ma wbudowane zabezpieczenie wątku, ale wymaga włączenia metod zliczania referencji (ref i deref), które można zrobić poprzez podklasowanie QSharedData. Podobnie jak w przypadku większości Qt, obiekty są najlepiej używane dzięki dużemu dziedziczeniu i podklasowaniu wszystko wydaje się być zamierzonym projektem.

QExplicitlySharedDataPointer - bardzo podobny do QSharedDataPointer z tym, że nie wywołuje domyślnie detach(). Nazwałbym tę wersję 2.0 QSharedDataPointer, ponieważ ten niewielki wzrost kontroli, kiedy dokładnie odłączyć się po spadku liczby referencji do zera, nie jest szczególnie wart całego nowego obiektu.

QSharedPointer - liczenie odniesień atomowych, bezpieczne wątki, wskaźnik współdzielony, usuwanie niestandardowe (obsługa tablic), brzmi jak wszystko, czym powinien być inteligentny wskaźnik. To jest to, czego używam przede wszystkim jako inteligentnego wskaźnika w Qt i uważam, że jest on porównywalny z boost:shared_ptr, chociaż prawdopodobnie znacznie więcej narzutu, jak wiele obiektów w Qt.

QWeakPointer - wyczuwasz powtarzający się wzór? Podobnie jak std::weak_ptr i boost::weak_ptr jest to używane w połączeniu z QSharedPointer, gdy potrzebujesz referencji między dwoma inteligentnymi wskaźnikami, które w przeciwnym razie spowodowałyby, że Twoje obiekty nigdy nie zostaną usunięte.

QScopedPointer - Nazwa ta powinna również wyglądać znajomo i w rzeczywistości była oparta na boost::scoped_ptr W przeciwieństwie do wersji Qt dzielonych i słabych wskaźników. Działa tak, aby zapewnić inteligentny wskaźnik jednego właściciela bez narzutu QSharedPointer, co czyni go bardziej odpowiednim do kompatybilności, bezpiecznego kodu wyjątku i wszystkich rzeczy, do których możesz użyć std::auto_ptr lub boost::scoped_ptr.

 221
Author: AJG85,
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
2013-03-25 21:20:38

Istnieje również Loki , który implementuje inteligentne wskaźniki oparte na zasadach.

Inne odniesienia do inteligentnych wskaźników opartych na zasadach, rozwiązujące problem słabego wsparcia optymalizacji pustej bazy wraz z wielokrotnym dziedziczeniem przez wiele kompilatorów:

 5
Author: Gregory Pakosz,
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
2011-09-02 23:42:51

Oprócz tych podanych, istnieją również pewne zorientowane na bezpieczeństwo:

SaferCPlusPlus

mse::TRefCountingPointer jest inteligentnym wskaźnikiem zliczającym odniesienia, takim jak std::shared_ptr. Różnica polega na tym, że mse::TRefCountingPointer jest bezpieczniejszy, mniejszy i szybszy, ale nie ma żadnego mechanizmu zabezpieczającego wątek. I występuje w wersjach" not null "I" fixed " (non-retargetable), które można bezpiecznie założyć, że zawsze wskazują na ważny obiekt. Więc zasadniczo, jeśli obiekt docelowy jest współdzielony między asynchroniczne wątki używają wtedy std::shared_ptr, w przeciwnym razie mse::TRefCountingPointer jest bardziej optymalne.

mse::TScopeOwnerPointer jest podobny do boost::scoped_ptr, ale działa w połączeniu z mse::TScopeFixedPointer w relacji "silny-słaby"podobnie jak std::shared_ptr i std::weak_ptr.

mse::TScopeFixedPointer wskazuje na obiekty, które są przydzielone na stosie lub których wskaźnik "posiadanie" jest przydzielany na stosie. Jego funkcjonalność jest (celowo) ograniczona w celu zwiększenia bezpieczeństwa w czasie kompilacji bez kosztów uruchamiania. Punkt" scope " pointers jest zasadniczo do określić zbiór okoliczności, które są na tyle proste i deterministyczne, że nie są konieczne żadne (uruchamiające) mechanizmy bezpieczeństwa.

mse::TRegisteredPointer zachowuje się jak surowy wskaźnik, z tą różnicą, że jego wartość jest automatycznie ustawiana na null_ptr, gdy obiekt docelowy zostanie zniszczony. Może być używany jako ogólny zamiennik surowych wskaźników w większości sytuacji. Podobnie jak surowy wskaźnik, nie ma żadnego wewnętrznego bezpieczeństwa wątku. Ale w zamian nie ma problemu z kierowaniem obiektów przydzielonych na stos (i uzyskiwaniem odpowiednie korzyści z wykonania). Po włączeniu sprawdzania czasu pracy wskaźnik ten jest bezpieczny przed dostępem do nieprawidłowej pamięci. Ponieważ mse::TRegisteredPointer zachowuje się tak samo jak surowy wskaźnik przy wskazywaniu ważnych obiektów, może być "wyłączony" (automatycznie zastąpiony odpowiednim surowym wskaźnikiem) za pomocą dyrektywy czasu kompilacji, pozwalając na użycie go do łapania błędów w trybach debugowania/testowania/beta, nie ponosząc żadnych kosztów ogólnych w trybie wydania.

Tutaj {[17] } jest artykuł opisujący dlaczego i jak z nich korzystać. (Uwaga, wtyczka bezwstydna.)

 1
Author: Noah,
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
2016-04-22 23:22:09