c++ c++11 smart-pointers auto-ptr

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:

  1. No todos los programadores leen libros o estándares
  2. auto_ptr a la vista, le promete la propiedad del objeto
  3. 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.
  4. 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 .