Perché auto_ptr è stato deprecato?


Risposte:


91

La sostituzione diretta per auto_ptr(o comunque la cosa più vicina a una) è unique_ptr. Per quanto riguarda il "problema", è piuttosto semplice: auto_ptrtrasferisce la proprietà quando viene assegnata. unique_ptrtrasferisce anche la proprietà, ma grazie alla codificazione della semantica del movimento e alla magia dei riferimenti rvalue, può farlo in modo molto più naturale. Inoltre "si adatta" notevolmente al resto della libreria standard (anche se, in tutta onestà, parte di ciò è dovuto al fatto che il resto della libreria cambia per adattarsi alla semantica dello spostamento invece di richiedere sempre la copia).

Anche il cambio di nome (IMO) è gradito: in auto_ptrrealtà non ti dice molto su ciò che tenta di automatizzare, mentre unique_ptrè una descrizione abbastanza ragionevole (se concisa) di ciò che viene fornito.


24
Solo una nota sul auto_ptrnome: auto suggerisce automatico come nella variabile automatica, e si riferisce a una cosa che auto_ptrfa: distruggere la risorsa gestita nel suo distruttore (quando esce dall'ambito).
Vincenzo Pii

13
Ulteriori informazioni: ecco la motivazione ufficiale per il ritiroauto_ptr : open-std.org/jtc1/sc22/wg21/docs/papers/2005/…
Howard Hinnant

@ HowardHinnant interessante doc! è strano in un certo senso che se std :: sort () è ha una specializzazione per std :: unique_ptr per usare la semantica di spostamento secondo necessità. Mi chiedo perché std :: sort () non possa essere specializzato per std :: auto_ptr per correggere il problema di copia menzionato nel documento. Grazie in anticipo.
Hei

2
@Hei: std::sortnon ha una specializzazione per unique_ptr. Invece è stato re-specificato di non copiare mai. Quindi funzionaauto_ptr davvero con il moderno . Ma il C ++ 98/03 è solo un algoritmo di esempio qui: qualsiasi algoritmo generico (fornito dallo standard o scritto dall'utente) che presume che la sintassi della copia abbia la semantica della copia probabilmente avrà un errore di runtime se usato con , perché si muove silenziosamente con sintassi di copia . Il problema è molto più ampio del semplice . sortsortauto_ptrauto_ptrsort
Howard Hinnant

35

Ho trovato ottime le risposte esistenti, ma dal PoV dei puntatori. IMO, una risposta ideale dovrebbe avere la risposta prospettica dell'utente / programmatore.

Per prima cosa (come sottolineato da Jerry Coffin nella sua risposta)

  • auto_ptr potrebbe essere sostituito da shared_ptr o unique_ptr a seconda della situazione

shared_ptr: se sei preoccupato per la liberazione di risorse / memoria E se hai più di una funzione che potrebbe utilizzare l'oggetto AT-DIFFERENT volte, allora vai con shared_ptr.

Con DIFFERENT-Times, pensa a una situazione in cui l'oggetto-ptr è memorizzato in più strutture dati e successivamente vi si accede. Più thread, ovviamente, è un altro esempio.

unique_ptr: se tutto ciò che ti interessa è liberare memoria e l'accesso all'oggetto è SEQUENZIALE, scegli unique_ptr.

Con SEQUENZIALE, intendo, in qualsiasi punto si accederà all'oggetto da un contesto. Ad esempio un oggetto che è stato creato e utilizzato immediatamente dopo la creazione dal creatore. Dopo la creazione l'oggetto viene memorizzato nella PRIMA struttura dati. Quindi o l'oggetto viene distrutto dopo l'UNICA struttura dati o viene spostato nella SECONDA struttura dati.

Da questa riga, farò riferimento a shared / unique _ptr come smart-pointer. (auto_ptr è anche smart-pointer MA a causa di difetti nel suo design, per i quali sono stati deprecati, e che penso indicherò nelle prossime righe, non dovrebbero essere raggruppati con smart-pointer.)

L'unico motivo più importante per cui auto_ptr è stato deprecato a favore di smart-pointer è la semantica dell'assegnazione Se non fosse stato per questo motivo, avrebbero aggiunto tutte le nuove chicche della semantica di spostamento ad auto_ptr invece di deprecarlo. Dato che la semantica di assegnazione era la caratteristica più antipatica, volevano che quella caratteristica sparisse, ma poiché è stato scritto del codice che usa quella semantica, (che il comitato degli standard non può cambiare), hanno dovuto lasciare andare auto_ptr, invece di modificandolo.

Dal link: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/

Tipo di incarichi supportati da unqiue_ptr

  • spostare assegnazione (1)
  • assegna un puntatore nullo (2)
  • incarico di tipo cast (3)
  • copia assegnazione (cancellato!) (4)

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

Tipo di incarichi supportati da auto_ptr

  • copia incarico (4) colpevole

Ora arrivando al motivo per cui l'assegnazione della copia stessa era così antipatica, ho questa teoria:

  1. Non tutti i programmatori leggono libri o standard
  2. auto_ptr a prima vista, ti promette la proprietà dell'oggetto
  3. la clausola little- * (gioco di parole) di auto_ptr, che non viene letta da tutti i programmatori, consente l'assegnazione di un auto_ptr a un altro e trasferisce la proprietà.
  4. La ricerca ha dimostrato che questo comportamento è destinato al 3,1415926535% di tutti gli utilizzi e non intenzionale in altri casi.

Il comportamento non intenzionale è davvero antipatico e quindi l'antipatia per auto_ptr.

(Per il 3,1415926536% dei programmatori che vuole intenzionalmente trasferire la proprietà C ++ 11 ha dato loro std :: move (), che ha reso la loro intenzione chiarissima per tutti gli stagisti che leggeranno e manterranno il codice.)


1
Dato che non vuoi mai che due auto_ptrvalori puntino allo stesso oggetto (dato che non danno la proprietà condivisa, il primo che muore lascerà l'altro con un'eredità letale; questo vale anche per l' unique_ptruso), puoi suggerire cosa era inteso in quelli rimanenti 96,8584073465% di tutto l'utilizzo?
Marc van Leeuwen

Non posso parlare per tutti loro, ma immagino che penserebbero che la proprietà dell'oggetto venga spostata e NON solo duplicata, il che è errato.
Ajeet Ganga,

@AjeetGanga Nella frase seguente "il piccolo- * (gioco di parole)", hai citato come "gioco di parole". Questa frase è nuova per me e comunque l'ho cercata su Google e ho saputo che c'è uno scherzo che è stato fatto apposta qui. Cos'è quello scherzo qui? Sono solo curioso di saperlo.
VINOTH ENERGETIC

@AjeetGanga Hai citato come "il piccolo- * (gioco di parole), clausola dell'auto_ptr, che non viene letta da tutti i programmatori, consente l'assegnazione di un auto_ptr a un altro e trasferisce la proprietà". Diciamo che ho due auto ptr come aeb per intero. Sto eseguendo l'assegnazione poiché *a=*b;qui solo il valore di b viene copiato in a. Spero che la proprietà sia di a che di b sia ancora con le stesse persone. Hai detto che la proprietà verrà trasferita. Come sarà?
VINOTH ENERGETIC

@VINOTHENERGETIC Ajeet stava parlando dell'assegnazione a un auto_ptroggetto stesso. L'assegnazione al / dal suo valore indicato non ha alcun effetto sulla proprietà né rilevanza per la proprietà. Spero che tu non stia ancora usando auto_ptr?
underscore_d

23

shared_ptrpossono essere conservati all'interno di contenitori. auto_ptrnon posso.

BTW unique_ptrè davvero la auto_ptrsostituzione diretta , combina le migliori caratteristiche di entrambi std::auto_ptre boost::scoped_ptr.


11

Ancora un altro modo di spiegare la differenza ...

Funzionalmente, il C ++ 11 std::unique_ptrè il "fisso" std::auto_ptr: entrambi sono adatti quando - in qualsiasi momento durante l'esecuzione - dovrebbe esserci un unico proprietario di puntatore intelligente per un oggetto puntato.

La differenza cruciale è nella costruzione della copia o nell'assegnazione da un altro puntatore intelligente senza scadenza, mostrato nelle =>righe seguenti:

   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

Sopra, ap3"ruba" silenziosamente la proprietà *ap, lasciandolo apimpostato su a nullptr, e il problema è che può accadere troppo facilmente, senza che il programmatore abbia pensato alla sua sicurezza.

Ad esempio, se un class/ structha un std::auto_ptrmembro, fare una copia di un'istanza farà sì che releaseil puntatore dell'istanza venga copiato: è una semantica strana e pericolosamente confusa poiché di solito copiare qualcosa non la modifica. È facile per l'autore della classe / struttura trascurare il rilascio del puntatore quando ragiona su invarianti e stato, e di conseguenza tenta accidentalmente di dereferenziare il puntatore intelligente mentre è null, o semplicemente non ha ancora previsto l'accesso / proprietà dei dati puntati.


auto_ptr "ruba" silenziosamente la proprietà +1
camino

3

auto_ptr non può essere utilizzato nei contenitori STL perché ha un costruttore di copie che non soddisfa i requisiti del contenitore CopyConstructible . unique_ptr non implementa un costruttore di copia, quindi i contenitori utilizzano metodi alternativi. unique_ptr può essere utilizzato nei contenitori ed è più veloce per gli algoritmi std rispetto a 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.