Dlaczego źle jest używać std :: auto_ptr <> ze standardowymi kontenerami?


217

Dlaczego korzystanie std::auto_ptr<>ze standardowych pojemników jest niewłaściwe ?


5
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/…
nickolay


1
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.
Phil1970

Niezły artykuł: Pojemniki STL i Auto_ptrs - Dlaczego nie mieszają quantstart.com/articles/…
alfC

Odpowiedzi:


124

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.

Aby obejść to ograniczenie, powinieneś użyć std::unique_ptr, std::shared_ptrlub std::weak_ptrinteligentnych wskaźników lub odpowiedników boost, jeśli nie masz C ++ 11. Oto dokumentacja biblioteki boost dla tych inteligentnych wskaźników.


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


39

Dwa bardzo doskonałe artykuły na ten temat:


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.


12

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.