Zdecydowanie +1 w tej sprawie, ponieważ widziałem, jak wiele osób popełnia błąd. To świetne pytanie.
twokats
Przeczytaj także powiązany temat. To pytanie jest rozpatrywane tutaj z drugiej strony. Przydaje się więcej informacji na temat kontenerów auto_ptr i STL. stackoverflow.com/questions/8630552/…
movesemantyczne i unique_ptrzostały zaprojektowane w celu uniknięcia problemów związanych z auto_ptr. W C ++ 03 język nie był wystarczająco silny, aby napisać taką klasę, auto_ptrktóra zachowuje się poprawnie i bezpiecznie we wszystkich scenariuszach, ponieważ kompilator i język nie były w stanie rozróżnić wartości l i r, więc użyto niektórych „hacków”, aby uzyskać pożądane zachowanie większość czasu.
Standard C ++ mówi, że element STL musi być „możliwy do skopiowania” i „możliwy do przypisania”. Innymi słowy, element musi mieć możliwość przypisania lub skopiowania, a oba elementy są logicznie niezależne. std::auto_ptrnie spełnia tego wymogu.
Weźmy na przykład ten kod:
class X{};
std::vector<std::auto_ptr<X>> vecX;
vecX.push_back(new X);
std::auto_ptr<X> pX = vecX[0];// vecX[0] is assigned NULL.
Powinieneś również rozważyć kontenery wskaźnika doładowania, jeśli nie potrzebujesz współwłasności.
me22
4
unique_ptrnie zezwala również na kopiowanie, więc niektóre operacje STL nie będą działały poprawnie, chyba że będą mogły użyć semantyki przenoszenia.
Mike Weller
4
„Aby obejść to ograniczenie, powinieneś użyć std::unique_ptr”: ten szablon klasy może istnieć tylko z powodu semantyki przenoszenia (jego specyfikacja wymaga odwołań do wartości), więc zasadniczo wymaga C ++ 11. Jednak (i powiązane) standard C ++ 11 nie mówi już, że typ elementu STL musi być „możliwy do skopiowania” i „przypisany”; wystarczająca jest możliwość konstruowania i przypisywania. Rzeczywiście, unique_ptrinstancje są tylko ruchome i można je przypisywać. Ale są też auto_ptrprzypadki! W rezultacie w C ++ 11 możesz zrobić z auto_ptrtym, co możesz zrobić unique_ptr.
Marc van Leeuwen
@MarcvanLeeuwen, chyba że reseti releasejeśli to konieczne
maniak ratchet
2
@ratchetfreak: Hmm, nie rozumiem. Co? „chyba że reseti release”, nie widzę, jak to się odnosi do czegokolwiek w moim komentarzu. Należy zauważyć, że zarówno auto_ptri unique_ptrmieć obie te metody, i robią to samo w obu przypadkach.
Marc van Leeuwen,
66
W semantyka kopiowania z auto_ptrnie są kompatybilne z pojemników.
W szczególności kopiowanie jednego auto_ptrdo drugiego nie tworzy dwóch równych obiektów, ponieważ jeden utracił własność wskaźnika.
Mówiąc dokładniej, skopiowanie auto_ptrpowoduje zwolnienie wskaźnika przez jedną z kopii. Który z nich pozostaje w kontenerze nie jest zdefiniowany. Dlatego możesz losowo utracić dostęp do wskaźników, jeśli przechowujesz je auto_ptrsw pojemnikach.
Ponieważ myślę, że w ciągu prawie dwóch lat prawdopodobnie zajął się tym problemem.
Szczeniak
27
@DeadMG: tak, masz rację. Ale to nie było moim celem. Jeśli ktoś kiedyś podejdzie do tego wątku i chce się dowiedzieć auto_ptrczegoś więcej, linki te będą pomocne, jestem pewien.
Lazer,
Istnieje wiele duplikatów, które są nowsze.
Szczeniak
8
@DeadMG: To pytanie nie zostało zamknięte jako duplikat, dlatego można je rozszerzyć. Lazer powiedział to, czego wcześniej tu nie powiedziano. Chyba przyszedł przypadkiem.
Sebastian Mach
Wyjaśnienia w drugim linku, które analizują problem po wywołaniu sort(), są bardziej przejrzyste niż wszystkie odpowiedzi tutaj.
chaosink
17
Kontenery STL muszą mieć możliwość kopiowania przechowywanych w nich przedmiotów i są zaprojektowane tak, aby oczekiwać, że oryginał i kopia będą równoważne. obiekty wskaźnika automatycznego mają zupełnie inną umowę, a kopiowanie powoduje przeniesienie własności. Oznacza to, że kontenery auto_ptr będą wykazywać dziwne zachowanie, w zależności od użycia.
Szczegółowy opis tego, co może pójść nie tak w Effective STL (Scott Meyers) pozycja 8, a także niezbyt szczegółowy opis w Effective C ++ (Scott Meyers) pozycja 13.
Kontenery STL przechowują kopie zawartych przedmiotów. Po skopiowaniu auto_ptr ustawia stary ptr na null. To zachowanie powoduje uszkodzenie wielu metod kontenerów.
Ale kiedy korzystasz z Unique_ptr, otrzymujesz prawie to samo, ponieważ tylko jeden Unique_ptr może mieć własność obiektu?
Tracer
2
@Tracer, unique_ptrjak każdy właściwy obiekt C ++ 11, może przenieść własność swojego zasobu tylko po skonstruowaniu lub przypisaniu, zapewniając, że programista musi celowo przekazać komendę std::move(sourceObject)tymczasową lub tymczasową, zamiast przekazywać wartość i nieinicjalnie / nieprzewidywalnie zmutować ją przez przypisanie kopii ... co, jak tutaj dokładnie podkreślono, było głównym problemem auto_ptr.
underscore_d
4
Norma C ++ 03 (ISO-IEC 14882-2003) mówi w klauzuli 20.4.5 pkt 3:
[...] [ Uwaga: [...] auto_ptr nie spełnia wymagań CopyConstructible i Assignable dla elementów kontenera biblioteki standardowej, a zatem utworzenie instancji kontenera biblioteki standardowej z auto_ptr powoduje niezdefiniowane zachowanie. - uwaga końcowa ]
Norma C ++ 11 (ISO-IEC 14882-2011) mówi w dodatku D.10.1 pkt 3:
[...]
Uwaga: [...] Instancje auto_ptr spełniają wymagania MoveConstructible i MoveAssignable, ale nie spełniają wymagań CopyConstructible i CopyAssignable. - uwaga końcowa]
Norma C ++ 14 (ISO-IEC 14882-2014) mówi w dodatku C.4.2 załącznik D: cechy kompatybilności:
Zmiana : Szablony klas auto_ptr, unary_function i binary_function, szablony funkcji random_shuffle oraz szablony funkcji (i ich typy zwracane) ptr_fun, mem_fun, mem_fun_ref, bind1st i bind2nd nie są zdefiniowane. Uzasadnienie : Zastąpione przez nowe funkcje. Wpływ na oryginalną funkcję : prawidłowy kod C ++ 2014 korzystający z tych szablonów klas i szablonów funkcji może nie zostać skompilowany w tym standardzie międzynarodowym.
move
semantyczne iunique_ptr
zostały zaprojektowane w celu uniknięcia problemów związanych zauto_ptr
. W C ++ 03 język nie był wystarczająco silny, aby napisać taką klasę,auto_ptr
która zachowuje się poprawnie i bezpiecznie we wszystkich scenariuszach, ponieważ kompilator i język nie były w stanie rozróżnić wartości l i r, więc użyto niektórych „hacków”, aby uzyskać pożądane zachowanie większość czasu.Odpowiedzi:
Standard C ++ mówi, że element STL musi być „możliwy do skopiowania” i „możliwy do przypisania”. Innymi słowy, element musi mieć możliwość przypisania lub skopiowania, a oba elementy są logicznie niezależne.
std::auto_ptr
nie spełnia tego wymogu.Weźmy na przykład ten kod:
Aby obejść to ograniczenie, powinieneś użyć
std::unique_ptr
,std::shared_ptr
lubstd::weak_ptr
inteligentnych wskaźników lub odpowiedników boost, jeśli nie masz C ++ 11. Oto dokumentacja biblioteki boost dla tych inteligentnych wskaźników.źródło
unique_ptr
nie zezwala również na kopiowanie, więc niektóre operacje STL nie będą działały poprawnie, chyba że będą mogły użyć semantyki przenoszenia.std::unique_ptr
”: ten szablon klasy może istnieć tylko z powodu semantyki przenoszenia (jego specyfikacja wymaga odwołań do wartości), więc zasadniczo wymaga C ++ 11. Jednak (i powiązane) standard C ++ 11 nie mówi już, że typ elementu STL musi być „możliwy do skopiowania” i „przypisany”; wystarczająca jest możliwość konstruowania i przypisywania. Rzeczywiście,unique_ptr
instancje są tylko ruchome i można je przypisywać. Ale są teżauto_ptr
przypadki! W rezultacie w C ++ 11 możesz zrobić zauto_ptr
tym, co możesz zrobićunique_ptr
.reset
irelease
jeśli to koniecznereset
irelease
”, nie widzę, jak to się odnosi do czegokolwiek w moim komentarzu. Należy zauważyć, że zarównoauto_ptr
iunique_ptr
mieć obie te metody, i robią to samo w obu przypadkach.W semantyka kopiowania z
auto_ptr
nie są kompatybilne z pojemników.W szczególności kopiowanie jednego
auto_ptr
do drugiego nie tworzy dwóch równych obiektów, ponieważ jeden utracił własność wskaźnika.Mówiąc dokładniej, skopiowanie
auto_ptr
powoduje zwolnienie wskaźnika przez jedną z kopii. Który z nich pozostaje w kontenerze nie jest zdefiniowany. Dlatego możesz losowo utracić dostęp do wskaźników, jeśli przechowujesz jeauto_ptrs
w pojemnikach.źródło
Dwa bardzo doskonałe artykuły na ten temat:
źródło
auto_ptr
czegoś więcej, linki te będą pomocne, jestem pewien.sort()
, są bardziej przejrzyste niż wszystkie odpowiedzi tutaj.Kontenery STL muszą mieć możliwość kopiowania przechowywanych w nich przedmiotów i są zaprojektowane tak, aby oczekiwać, że oryginał i kopia będą równoważne. obiekty wskaźnika automatycznego mają zupełnie inną umowę, a kopiowanie powoduje przeniesienie własności. Oznacza to, że kontenery auto_ptr będą wykazywać dziwne zachowanie, w zależności od użycia.
Szczegółowy opis tego, co może pójść nie tak w Effective STL (Scott Meyers) pozycja 8, a także niezbyt szczegółowy opis w Effective C ++ (Scott Meyers) pozycja 13.
źródło
Kontenery STL przechowują kopie zawartych przedmiotów. Po skopiowaniu auto_ptr ustawia stary ptr na null. To zachowanie powoduje uszkodzenie wielu metod kontenerów.
źródło
unique_ptr
jak każdy właściwy obiekt C ++ 11, może przenieść własność swojego zasobu tylko po skonstruowaniu lub przypisaniu, zapewniając, że programista musi celowo przekazać komendęstd::move(sourceObject)
tymczasową lub tymczasową, zamiast przekazywać wartość i nieinicjalnie / nieprzewidywalnie zmutować ją przez przypisanie kopii ... co, jak tutaj dokładnie podkreślono, było głównym problememauto_ptr
.Norma C ++ 03 (ISO-IEC 14882-2003) mówi w klauzuli 20.4.5 pkt 3:
Norma C ++ 11 (ISO-IEC 14882-2011) mówi w dodatku D.10.1 pkt 3:
Norma C ++ 14 (ISO-IEC 14882-2014) mówi w dodatku C.4.2 załącznik D: cechy kompatybilności:
źródło