2016-05-22 12 views
7

Po fakcie, biorąc pod uwagę make_shared, czy shared_ptr ma konstruktor, który pobiera surowy wskaźnik, gdyby został wprowadzony w C++ 11?był konstruktorem wskaźnika surowego z shared_ptr a błąd?

Czy istnieją silne argumenty lub przypadki użycia na korzyść tego konstruktora?

Unikałoby dobrze udokumentowanej pułapki exception-safety i memory allocation/performance advantage korzystania z make_shared.

wierzę kolejną zaletę, że wymaga shared_ptr budowę poprzez make_shared byłoby, że może to być pojedynczy wskaźnik pod maską, obniżając jego wykorzystanie pamięci i dokonując rzeczy jak atomic_compare_exchange dużo prostsze (i ewentualnie bardziej wydajne). (Patrz presentation from C++Now)

EDIT

Rozumiem, że shared_ptr, że w zasadzie jest intrusive_ptr (z przedmiotem i bloku sterowania coalesced) brakowałoby dysponuje aktualną std :: shared_ptr ma. Jak:

  1. zdolność do uwolnienia przedmiotu osobno z bloku sterowania (co jest miłe, jeśli od dawna mieszkał weak_ptrs)

  2. zgodność z bibliotekami że ręka ci surowych wskaźników i odpowiedzialność, aby je uwolnić

  3. zdolność do przechowywania dowolnych zasobów z niestandardowymi deleters (lub bez Deleter, dla osób nie posiadających wskaźników)

  4. możliwość wskazują na sub-object (np członk er) przy jednoczesnym utrzymaniu żywego obiektu nadrzędnego.

Co mam co sugeruje, że te funkcje nie mogą być wykorzystywane na tyle często (lub w przypadku użycia go jako RAII-wrapper) mogą nie być najlepsze dopasowanie, aby uzasadnić dodatkowych kosztów:

  1. oddzielny wskaźnik do bloku sterowania
  2. (potencjalnie) bardziej skomplikowanej logiki atomic_compare_exchange, nie może być warto.

W C++ 98 świecie (gdzie shared_ptr został wprowadzony) make_shared jest mniej praktyczny i przyjazny mniej użytkownika (brak idealnego spedycji wymaga owijarki referencyjnych oraz brak zmiennej liczbie argumentów szablonów sprawia, że ​​realizacja przylegający).

+2

Co, jeśli nie kontrolujesz konstrukcji obiektu? (Powiedzmy, że zarządzasz zasobem z biblioteki C). –

+0

'make_shared' został wprowadzony w C++ 11. –

+0

@NicolBolas prawy, i shared_ptr został wprowadzony w C++ 03 – Arvid

Odpowiedz

12

Problem z logiki jest przekonanie, że powodem shared_ptr ma rozróżnienia między zarządzanego wskaźnika i wskaźnika get dlatego make_shared nie był dostępny. Dlatego też, jeśli zmuszamy wszystkich do używania make_shared do tworzenia shared_ptr, nie potrzebowalibyśmy tego rozróżnienia.

To jest nieprawidłowe.

można zaimplementować konstruktor wskaźnika opartego shared_ptr „s bez tego rozróżnienia. W końcu w początkowym utworzeniu zarządzanego shared_ptr, wskaźnik get i zarządzany wskaźnik są takie same. Jeśli chcesz, aby shared_ptr było sizeof(T*), możesz po prostu pobrać shared_ptr ze wskaźnika get z zarządzanego bloku. Jest to niezależne od tego, czy T jest osadzony w zarządzanym bloku.

więc rozróżnienie naprawdę ma w ogóle nic wspólnego zmake_shared i jego zdolność do osadzić T obrębie tej samej pamięci jako zarządzanego bloku. A raczej jego brak.

Nie, rozróżnienie pomiędzy zarządzanym wskaźnika i wskaźnika get został stworzony, ponieważ dodaje Features do shared_ptr. Ważne. Ty wymienione niektóre z nich, ale brakowało innym:

  • Zdolność mieć shared_ptr do klasy bazowej. Czyli:

    shared_ptr<base> p = make_shared<derived>(...); 
    

    Aby to zrobić, musi mieć rozróżnienie pomiędzy tym, co poszczególne punkty instancji i co Kontrole bloku sterowania.

  • static_pointer_cast i dynamic_pointer_cast (i reinterpret_pointer_cast w C++ 17). Wszystkie one polegają na rozróżnieniu między zarządzanym wskaźnikiem a wskaźnikiem get.

    • Dotyczy to również enable_shared_from_this w klasach bazowych.
  • A shared_ptr wskazujący na podobiektu członkowskim typu, który sam w sobie jest zarządzany przez shared_ptr. Ponownie wymaga to, aby zarządzany wskaźnik nie był taki sam jak wskaźnik get.

Wydaje się również, że lekceważysz możliwość zarządzania wskaźnikami, które nie zostały utworzone przez Ciebie. Jest to zdolność krytyczna, ponieważ pozwala ona na kompatybilność z innymi bazami kodów. Wewnętrznie, możesz użyć shared_ptr do zarządzania rzeczami utworzonymi przez bibliotekę, która została napisana w 1998 roku.

Na swój sposób dzielisz kod na dwie epoki: pre-C++ 11 i post-C++ 11. Twój shared_ptr nic nie zrobi dla żadnego kodu jawnie napisanego dla C++ 11.

A rzeczą zawijania wszystkie te cechy się do jednego typu to:

Nie potrzeba jeszcze jednego.

shared_ptr, ponieważ zaspokaja tak wiele potrzeb, może być skutecznie wykorzystany niemal wszędzie. Możliwe, że nie jest to absolutnie najbardziej efektywny typ, ale w każdym przypadku wykonuje to zadanie. I nie jest to do końca powolne.

Obsługuje współwłasność z polimorfizmem. Obsługuje wspólną własność obiektów członkowskich. Obsługuje współdzielone prawa własności do pamięci, której nie przydzielono. Obsługuje wspólną własność pamięci ze specjalnymi potrzebami alokacji/dealokacji. I tak dalej.

Jeśli potrzebujesz semantyki współwłasności i potrzebujesz jej do , praca, shared_ptr ma za każdym razem twoich pleców. Dzięki zasugerowanemu pomysłowi zawsze pojawiałyby się ograniczenia, coś, co przeszkadzałoby ci w wykonaniu pracy.

Typ, który działa, powinien być domyślnie preferowany nad typem, który nie działa.

+0

dzięki! robisz wiele dobrych punktów – Arvid

13

Z perspektywy czasu, biorąc pod uwagę make_shared, by shared_ptr mieć konstruktora, że ​​trwa wskaźnik surowy, gdyby zostały wprowadzone z C++ 11?

Co zrobić, jeśli nie kontrolujesz przydziału obiektu? A co jeśli potrzebujesz niestandardowego narzędzia do usuwania? Co jeśli potrzebujesz inicjałów list zamiast parens?

Żaden z tych przypadków nie jest obsługiwany przez make_shared.

Dodatkowo, jeśli używasz weak_ptr, A shared_ptr przydzielane przez make_shared nie zwolni żadnej pamięci, aż wszystkie weak_ptr s są zniszczone, jak również. Więc nawet jeśli masz normalny współdzielony wskaźnik tam, gdzie nie ma żadnego z powyższych, możliwe jest, że możesz nadal preferować konstruktor wskaźnika surowego.

Jeszcze inna sytuacja byłaby, gdyby Twój typ zapewniał przeciążenia dla operator new i operator delete. Może to sprawić, że będzie źle pasował do make_shared, ponieważ przeciążenia te nie będą wywoływane - i prawdopodobnie istnieją z jakiegoś powodu.

+2

Polecam OP czyta Efektywny Modern C++ Scotta Myera do dokładnego opisu racjonalnego uzasadnienia projektu. – marko

+0

Po tym wszystkim, 'make_shared' jest prawdopodobnie normalnym przypadkiem, a wskaźnik surowego wskaźnika jest wyjątkowy. –

+1

@Martin Pewnie, ale jest duża różnica między powiedzeniem, że powinieneś preferować 'make_shared' i sugerowanie, że alternatywa jest wadą projektu. – Barry

3

std::shared_ptr robi znacznie więcej niż przeznaczyć obiektów na stercie.

rozważyć jego zastosowanie jako auto-zamykania wspólnego uchwytu pliku:

#include <cstdio> 
#include <memory> 


int main() 
{ 
    auto closer = [](FILE* fp) { std::fclose(fp); }; 
    auto fp = std::shared_ptr<FILE>(std::fopen("foo.txt", "r"), 
            closer); 
} 
+3

"it's" jest skrótem od "it is". To nie jest forma dzierżawcza. –

+1

@LightnessRacesinOrbit mea culpa! –

Powiązane problemy