Dlaczego auto_ptr jest przestarzałe?

Odpowiedzi:

93

Bezpośrednim zamiennikiem auto_ptr(lub i tak najbliższym temu) jest unique_ptr. Jeśli chodzi o „problem”, sprawa jest całkiem prosta: auto_ptrprzenosi własność, gdy jest przypisana. unique_ptrprzenosi również własność, ale dzięki kodyfikacji semantyki ruchu i magii odniesień do wartości r może to zrobić znacznie bardziej naturalnie. Znacznie lepiej „pasuje” do reszty biblioteki standardowej (choć, uczciwie mówiąc, część z nich jest spowodowana zmianą reszty biblioteki w celu dostosowania do semantyki przenoszenia, zamiast konieczności ciągłego kopiowania).

Zmiana nazwy jest również (IMO) mile widziana - auto_ptrtak naprawdę niewiele mówi o tym, co próbuje zautomatyzować, a unique_ptrjest dość rozsądnym (choć zwięzłym) opisem tego, co jest dostarczane.

Jerry Coffin
źródło
25
Tylko uwaga przy auto_ptrnazwie: auto sugeruje automatyczne, jak w zmiennej automatycznej, i odnosi się do jednej rzeczy, która auto_ptrrobi: zniszczenie zarządzanego zasobu w jego destruktorze (kiedy wyjdzie poza zakres).
Vincenzo Pii,
14
Dalsze informacje: Oto oficjalne uzasadnienie wycofania auto_ptr: open-std.org/jtc1/sc22/wg21/docs/papers/2005/…
Howard
@HowardHinnant ciekawy dokument! dziwne jest w pewnym sensie, że jeśli std :: sort () ma specjalizację std :: unique_ptr, aby w razie potrzeby używać semantyki przenoszenia. Zastanawiam się, dlaczego std :: sort () nie może być wyspecjalizowane w std :: auto_ptr, aby poprawić problem z kopiowaniem wspomniany w dokumencie. Z góry dziękuję.
Hei
2
@Hei: std::sortnie ma specjalizacji dla unique_ptr. Zamiast tego określono ponownie, aby nigdy nie kopiować. Więc auto_ptrfaktycznie robi pracę z nowoczesnym sort. Ale C ++ 98/03 sortto tylko przykładowy algorytm tutaj: Każdy algorytm ogólny (dostarczony standardowo lub napisany przez użytkownika), który zakłada, że ​​składnia kopiowania ma semantykę kopiowania, prawdopodobnie będzie miał błąd czasu wykonania, jeśli zostanie użyty z auto_ptr, ponieważ auto_ptrcicho porusza się ze składnią kopiowania . Problem jest znacznie większy niż tylko sort.
Howard Hinnant
36

Uważam, że istniejące odpowiedzi są świetne, ale z punktu początkowego wskaźników. IMO, idealna odpowiedź powinna mieć odpowiedź z perspektywy użytkownika / programisty.

Po pierwsze (jak wskazał Jerry Coffin w swojej odpowiedzi)

  • auto_ptr może zostać zastąpione przez shared_ptr lub unique_ptr w zależności od sytuacji

shared_ptr: Jeśli obawiasz się zwolnienia zasobów / pamięci ORAZ jeśli masz więcej niż jedną funkcję, która może używać obiektu AT-RIFFERENT razy, przejdź do shared_ptr.

Przez DIFFERENT-Times wyobraź sobie sytuację, w której obiekt-ptr jest przechowywany w wielu strukturach danych i później dostępny. Oczywiście wiele wątków to kolejny przykład.

unique_ptr: Jeśli wszystko, co cię niepokoi, to zwolnienie pamięci, a dostęp do obiektu jest SEQUENTIAL, przejdź do unique_ptr.

Przez SEQUENTIAL mam na myśli, że w dowolnym momencie dostęp do obiektu będzie możliwy z jednego kontekstu. Np. Obiekt, który został utworzony i użyty natychmiast po utworzeniu przez twórcę. Po utworzeniu obiekt jest przechowywany w strukturze danych FIRST . Następnie obiekt jest niszczony po JEDNEJ strukturze danych lub przenoszony do DRUGIEJ struktury danych.

W tym wierszu będę odnosić się do shared / unique _ptr jako smart-pointers. (auto_ptr jest również smart-pointer, ALE z powodu błędów w jego projekcie, dla których są przestarzałe i które myślę, że wskażę w następnych wierszach, nie powinny być grupowane ze smart-pointer.)

Jednym z najważniejszych powodów, dla których auto_ptr zostało uznane za przestarzałe na korzyść smart-pointer, jest semantyka przypisania. Gdyby nie z tego powodu, dodaliby wszystkie nowe zalety semantyki przenoszenia do auto_ptr zamiast go wycofywać. Ponieważ semantyka przypisania była najbardziej nielubiana, chcieli, aby ta funkcja zniknęła, ale ponieważ istnieje kod, który używa tej semantyki (której komisja normalizacyjna nie może zmienić), musieli puścić auto_ptr, zamiast modyfikowanie go.

Z linku: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/

Rodzaj zadań obsługiwanych przez unqiue_ptr

  • przenieś zadanie (1)
  • przypisać wskaźnik zerowy (2)
  • przypisanie typu (3)
  • przypisanie kopii (usunięte!) (4)

Od: http://www.cplusplus.com/reference/memory/auto_ptr/operator=/

Rodzaj zadań obsługiwanych przez auto_ptr

  • Przypisanie kopia (4) winny

Przechodząc teraz do powodu DLACZEGO samo przypisanie kopii było tak nielubiane, mam następującą teorię:

  1. Nie wszyscy programiści czytają książki lub standardy
  2. auto_ptr na pierwszy rzut oka obiecuje własność obiektu
  3. klauzula little- * (gra słów zamierzona) auto_ptr, która nie jest czytana przez wszystkich programistów, pozwala na przypisanie jednego auto_ptr do drugiego i przenosi własność.
  4. Badania wykazały, że to zachowanie jest przeznaczone dla 3,1415926535% całego użycia i niezamierzone w innych przypadkach.

Niezamierzone zachowanie jest naprawdę nielubiane, stąd niechęć do auto_ptr.

(Dla 3.1415926536% programistów, którzy celowo chcą przenieść własność C ++ 11, dało im std :: move (), co jasno pokazało ich zamiar wszystkim stażystom, którzy będą czytać i utrzymywać kod.)

Ajeet Ganga
źródło
1
Ponieważ nigdy nie chcesz, aby dwie auto_ptrwartości wskazywały na ten sam obiekt (ponieważ nie dają one współwłasności, pierwsza, która umrze, pozostawi drugą ze śmiercionośnym dziedzictwem; dotyczy to również unique_ptrużycia), czy możesz zasugerować, co było zamierzone w pozostałe 96,8584073465% całego wykorzystania?
Marc van Leeuwen
Nie mogę mówić za nich wszystkich, ale sądzę, że pomyśleliby, że własność obiektu jest przenoszona, a NIE tylko powielana, co jest błędne.
Ajeet Ganga
@AjeetGanga W poniższym zdaniu „mały- * (gra słów przeznaczona)” wspomniałeś jako „zamierzona gra słów”. To zdanie jest dla mnie nowe, a mimo to wyszukałem je w Google i dowiedziałem się, że jest jakiś żart, który został tutaj specjalnie zrobiony. Co to za żart? Ciekawe, że to wiem.
VINOTH ENERGETIC
@AjeetGanga Wspomniałeś jak „mała- * (gra słów zamierzona), klauzula auto_ptr, która nie jest odczytywana przez wszystkich programistów, zezwala na przypisanie jednego auto_ptr do drugiego i przenosi własność”. Powiedzmy, że mam dwa auto ptr jako a i b do liczby całkowitej. Wykonuję przypisanie, ponieważ *a=*b;tutaj tylko wartość b jest kopiowana do a. Mam nadzieję, że zarówno a, jak i b są nadal własnością tych samych osób. Wspomniałeś, że własność zostanie przeniesiona. Jak to będzie?
VINOTH ENERGETIC
@VINOTHENERGETIC Ajeet mówił o przypisywaniu do auto_ptrsamego obiektu. Przypisywanie do / z jej wskazanej wartości nie ma wpływu na własność ani nie ma dla niej znaczenia. Mam nadzieję, że nadal nie używasz auto_ptr?
underscore_d
23

shared_ptrmożna przechowywać w pojemnikach. auto_ptrżargon.

BTW unique_ptrto naprawdę bezpośredni auto_ptrzamiennik, łączy w sobie najlepsze cechy obu std::auto_ptri boost::scoped_ptr.

Ben Voigt
źródło
11

Jeszcze jedno podejście do wyjaśnienia różnicy ...

Funkcjonalnie, C ++ 11 std::unique_ptrjest „ustalonym” std::auto_ptr: oba są odpowiednie, gdy - w dowolnym momencie podczas wykonywania - powinien istnieć jeden właściciel inteligentnego wskaźnika dla wskazanego obiektu.

Istotna różnica polega na tworzeniu kopii lub przypisywaniu z innego nie wygasającego inteligentnego wskaźnika, pokazanego w =>liniach poniżej:

   std::auto_ptr<T> ap(...);
   std::auto_ptr<T> ap2(get_ap_to_T());   // take expiring ownership
=> std::auto_ptr<T> ap3(ap);  // take un-expiring ownership ala ap3(ap.release());
   ap->xyz;  // oops... can still try to use ap, expecting it to be non-NULL

   std::unique_ptr<T> up(...);
   std::unique_ptr<T> up2(get_up_to_T());   // take expiring ownership
=> std::unique_ptr<T> up3(up);  // COMPILE ERROR: can't take un-expiring ownership
=> std::unique_ptr<T> up4(std::move(up));  // EXPLICIT code allowed
=> std::unique_ptr<T> up4(up.release());   // EXPLICIT code allowed

Powyżej, ap3po cichu „kradnie” własność *ap, pozostawiając apustawienie a nullptr, a problem polega na tym, że może się to zdarzyć zbyt łatwo, bez przemyślenia przez programistę jego bezpieczeństwa.

Na przykład, jeśli class/ structma std::auto_ptrskładową, utworzenie kopii instancji spowoduje releasewyświetlenie wskaźnika z kopiowanej instancji: to dziwna i niebezpiecznie myląca semantyka, ponieważ zwykle kopiowanie czegoś jej nie modyfikuje. Autorowi klasy / struktury łatwo jest przeoczyć zwolnienie wskaźnika podczas wnioskowania o niezmiennikach i stanie, aw konsekwencji przypadkowo podjąć próbę wyłuskiwania inteligentnego wskaźnika, gdy ma wartość null, lub po prostu nie spodziewał się dostępu / własności wskazanych danych.

Tony Delroy
źródło
auto_ptr po cichu „kradnie” własność +1
camino
3

Auto_ptr nie może być używany w kontenerach STL, ponieważ ma konstruktor kopiujący, który nie spełnia wymagań kontenera CopyConstructible . unique_ptr nie implementuje konstruktora kopiującego, więc kontenery używają alternatywnych metod. unique_ptr może być używany w kontenerach i jest szybszy dla algorytmów std niż shared_ptr.

#include <iostream>
#include <type_traits>
#include <vector>
#include <memory>

using namespace std;

int main() {
  cout << boolalpha;
  cout << "is_copy_constructible:" << endl;
  cout << "auto_ptr: " << is_copy_constructible< auto_ptr<int> >::value << endl;
  cout << "unique_ptr: " << is_copy_constructible< unique_ptr<int> >::value << endl;
  cout << "shared_ptr: " << is_copy_constructible< shared_ptr<int> >::value << endl;

  vector<int> i_v;
  i_v.push_back(1);
  cout << "i_v=" << i_v[0] << endl;
  vector<int> i_v2=i_v;
  cout << "i_v2=" << i_v2[0] << endl;

  vector< unique_ptr<int> > u_v;
  u_v.push_back(unique_ptr<int>(new int(2)));
  cout << "u_v=" << *u_v[0] << endl;
  //vector< unique_ptr<int> > u_v2=u_v;  //will not compile, need is_copy_constructible == true
  vector< unique_ptr<int> > u_v2 =std::move(u_v);  // but can be moved
  cout << "u_v2=" << *u_v2[0] << " length u_v: " <<u_v.size() << endl;

  vector< shared_ptr<int> > s_v;
  shared_ptr<int> s(new int(3));
  s_v.push_back(s);
  cout << "s_v=" << *s_v[0] << endl;
  vector< shared_ptr<int> > s_v2=s_v;
  cout << "s_v2=" << *s_v2[0] << endl;

  vector< auto_ptr<int> > a_v;  //USAGE ERROR

  return 0;
}

>cxx test1.cpp -o test1
test1.cpp: In function âint main()â:
test1.cpp:33:11: warning: âauto_ptrâ is deprecated (declared at /apps/hermes/sw/gcc/gcc-4.8.5/include/c++/4.8.5/backward/auto_ptr.h:87) [-Wdeprecated-declarations]
   vector< auto_ptr<int> > a_v;  //USAGE ERROR
           ^
>./test1
is_copy_constructible:
auto_ptr: false
unique_ptr: false
shared_ptr: true
i_v=1
i_v2=1
u_v=2
s_v=3
s_v2=3
edW
źródło