c++ - ¿Por qué auto_ptr está en desuso?
c++11 smart-pointers (5)
El reemplazo directo para auto_ptr
(o lo más parecido a uno de todos modos) es unique_ptr
. En lo que respecta al "problema", es bastante simple: auto_ptr
transfiere la propiedad cuando se asigna. unique_ptr
también transfiere la propiedad, pero gracias a la codificación de la semántica de movimientos y la magia de las referencias rvalue, puede hacerlo de forma mucho más natural. También "encaja" con el resto de la biblioteca estándar considerablemente mejor (aunque, para ser justos, parte de eso se debe a que el resto de la biblioteca cambia para adaptarse a la semántica de movimientos en lugar de requerir siempre la copia).
El cambio de nombre también es (IMO) bienvenido: auto_ptr
realmente no dice mucho sobre lo que intenta automatizar, mientras que unique_ptr
es una descripción bastante razonable (aunque concisa) de lo que se proporciona.
Escuché que auto_ptr
está en desuso en C ++ 11. ¿Cuál es la razón para esto?
También me gustaría saber la diferencia entre auto_ptr
y shared_ptr
.
Encontré excelentes las respuestas existentes, pero desde el PoV de los indicadores. IMO, una respuesta ideal debe tener la respuesta de perspectiva del usuario / programador.
Primero lo primero (como señaló Jerry Coffin en su respuesta)
- auto_ptr podría ser reemplazado por shared_ptr o unique_ptr dependiendo de la situación
shared_ptr: si le preocupa la liberación de recursos / memoria Y si tiene más de una función que podría usar el objeto AT-DIFFERENT veces, vaya con shared_ptr.
En DIFERENTES veces, piense en una situación donde el objeto-ptr se almacena en una estructura de datos múltiple y luego se accede a él. Múltiples hilos, por supuesto, es otro ejemplo.
unique_ptr: si lo único que le preocupa es liberar memoria, y el acceso al objeto es SECUENCIAL, vaya a unique_ptr.
Por SECUENCIAL, quiero decir, en cualquier punto se accederá a un objeto desde un contexto. Por ejemplo, un objeto que fue creado, y utilizado inmediatamente después de la creación por el creador. Después de la creación, el objeto se almacena en FIRST data-structure. Entonces, o bien el objeto se destruye después de la estructura de datos ONE o se mueve a SECOND data-structure.
Desde esta línea, haré referencia a los _ptr compartidos / únicos como punteros inteligentes. (auto_ptr también es un puntero inteligente PERO debido a fallas en su diseño, por lo que están en desuso, y que creo que señalaré en las siguientes líneas, no deberían agruparse con puntero inteligente).
La razón más importante por la que auto_ptr quedó en desuso en favor del puntero inteligente es la semántica de asignación. Si no fuera por esa razón, habrían agregado todas las ventajas de la semántica de movimientos a auto_ptr en lugar de desaprobarla. Como la semántica de asignación era la característica más desagradable, querían que esa característica desapareciera, pero dado que hay un código escrito que usa esa semántica (que el comité de normas no puede cambiar), tuvieron que soltar auto_ptr, en lugar de modificándolo.
Desde el enlace: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/
Tipo de asignaciones admitidas por unqiue_ptr
- mover asignación (1)
- Asignar puntero nulo (2)
- asignación de tipografía (3)
- copia de asignación (¡eliminado!) (4)
De: http://www.cplusplus.com/reference/memory/auto_ptr/operator=/
Tipo de asignaciones admitidas por auto_ptr
- copia asignación (4) culpable
Ahora, llegando a la razón por la cual la asignación de copia en sí misma era tan desagradable, tengo esta teoría:
- No todos los programadores leen libros o estándares
- auto_ptr a la vista, le promete la propiedad del objeto
- la cláusula little- * (juego de palabras) de auto_ptr, que no es leída por todos los programadores, permite la asignación de un auto_ptr a otro y transfiere la propiedad.
- La investigación ha demostrado que este comportamiento está destinado al 3.1415926535% del uso total y no intencional en otros casos.
El comportamiento no deseado es realmente desagradable y, por lo tanto, el desagrado por el auto_ptr.
(Para el 3.1415926536% de los programadores que intencionalmente quieren transferir la propiedad C ++ 11 les dio std :: move (), lo que hizo que su intención fuera clara para todos los internos que iban a leer y mantener el código).
Otra explicación para explicar la diferencia ...
Funcionalmente, std::unique_ptr
C ++ 11 es el std::unique_ptr
"fijo" std::auto_ptr
: ambos son adecuados cuando, en cualquier momento durante la ejecución, debe haber un único propietario de puntero inteligente para un objeto apuntado .
La diferencia crucial está en la construcción de copias o en la asignación desde otro puntero inteligente que no expira, que se muestra en la =>
líneas a continuación:
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
Arriba, ap3
silenciosamente "roba" la propiedad de *ap
, dejando ap
establecido en nullptr
, y el problema es que puede suceder muy fácilmente, sin que el programador haya pensado en su seguridad.
Por ejemplo, si una class
/ struct
tiene un miembro std::auto_ptr
, entonces hacer una copia de una instancia release
el puntero de la instancia que se está copiando: eso es una semántica extraña y peligrosamente confusa ya que generalmente copiar algo no lo modifica. Es fácil para el autor de la clase / estructura pasar por alto el lanzamiento del puntero al razonar sobre invariantes y el estado, y consecuentemente intentar accidentalmente desreferenciar el puntero inteligente mientras está nulo, o simplemente no tener el acceso / propiedad esperado de los datos apuntados.
auto_ptr no se puede usar en contenedores STL porque tiene un constructor de copia que no cumple los requisitos del contenedor CopyConstructible . unique_ptr no implementa un constructor de copia, por lo que los contenedores utilizan métodos alternativos. unique_ptr se puede usar en contenedores y es más rápido para algoritmos std que 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
shared_ptr
se puede almacenar dentro de los contenedores. auto_ptr
no puede.
BTW unique_ptr
es realmente el reemplazo directo de auto_ptr
, combina las mejores características de std::auto_ptr
y boost::scoped_ptr
.