¿Por qué se desaprueba auto_ptr?

Respuestas:

91

El reemplazo directo de 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_ptrtransfiere la propiedad cuando se asigna. unique_ptrtambié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 considerablemente más natural. También "encaja" con el resto de la biblioteca estándar considerablemente mejor (aunque, para ser justos, algo de eso se debe a que el resto de la biblioteca cambió para adaptarse a la semántica de movimiento en lugar de requerir siempre copia).

El cambio de nombre también es (en mi opinión) bienvenido: en auto_ptrrealidad no le dice mucho sobre lo que intenta automatizar, mientras que unique_ptres una descripción bastante razonable (aunque concisa) de lo que se proporciona.

Jerry Coffin
fuente
24
Solo una nota sobre el auto_ptrnombre: auto sugiere automático como variable automática, y se refiere a una cosa que auto_ptrhace: destruir el recurso administrado en su destructor (cuando sale del alcance).
Vincenzo Pii
13
Más información: Aquí está la justificación oficial para desaprobar auto_ptr: open-std.org/jtc1/sc22/wg21/docs/papers/2005/…
Howard Hinnant
@HowardHinnant ¡doc interesante! es extraño en cierto sentido que si std :: sort () tiene una especialización para std :: unique_ptr para usar la semántica de movimiento según sea necesario. Me pregunto por qué std :: sort () no puede estar especializado para std :: auto_ptr para corregir el problema de copia mencionado en el documento. Gracias por adelantado.
Hei
2
@Hei: std::sortno tiene especialización en unique_ptr. En cambio, se volvió a especificar para no copiar nunca. Así que auto_ptren realidad hace el trabajo con el moderno sort. Pero el C ++ 98/03 sortes solo un algoritmo de ejemplo aquí: cualquier algoritmo genérico (proporcionado por el estándar o escrito por el usuario) que asume que la sintaxis de copia tiene semántica de copia probablemente tendrá un error de tiempo de ejecución si se usa con auto_ptr, porque auto_ptrse mueve silenciosamente con sintaxis de copia . El problema es mucho más grande que solo sort.
Howard Hinnant
35

Encontré excelentes respuestas existentes, pero desde el punto de vista de los punteros. En mi opinión, una respuesta ideal debe tener la respuesta de perspectiva del usuario / programador.

Primero lo primero (como lo 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 estar usando el objeto AT-DIFFERENT veces, vaya con shared_ptr.

Por DIFFERENT-Times, piense en una situación en la que el objeto-ptr se almacena en múltiples estructuras de datos y luego se accede a él. Varios hilos, por supuesto, es otro ejemplo.

unique_ptr: Si lo único que le preocupa es liberar memoria y el acceso al objeto es SECUENCIAL, elija unique_ptr.

Por SECUENCIAL, quiero decir, en cualquier punto se accederá al 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 la PRIMERA estructura de datos. Luego, el objeto se destruye después de la UNA estructura de datos o se mueve a la SEGUNDA estructura de datos.

Desde esta línea, me referiré a _ptr compartido / único 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 deben agruparse con el puntero inteligente).

La única razón más importante de por qué auto_ptr se desaprobó en favor de smart-pointer es la semántica de asignación. Si no fuera por esa razón, habrían agregado todas las novedades de la semántica de movimiento a auto_ptr en lugar de desaprobarlo. Dado que la semántica de asignación era la característica más desagradable, querían que esa característica desapareciera, pero como hay código escrito que usa esa semántica (que el comité de estándares no puede cambiar), tuvieron que dejar de lado auto_ptr, en lugar de modificarlo.

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 tipográfica (3)
  • copiar asignación (eliminada!) (4)

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

Tipo de asignaciones admitidas por auto_ptr

  • copiar asignación (4) culpable

Ahora, llegando a la razón por la que la asignación de copia en sí misma no le gustó tanto, tengo esta teoría:

  1. No todos los programadores leen libros o estándares
  2. auto_ptr en la cara, le promete la propiedad del objeto
  3. la pequeña- * (juego de palabras), cláusula del 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. Las investigaciones han demostrado que este comportamiento está destinado al 3,1415926535% de todo el uso y no previsto en otros casos.

El comportamiento no deseado es realmente desagradable y, por lo tanto, el desagrado por auto_ptr.

(Para el 3.1415926536% de los programadores que intencionalmente quieren transferir la propiedad, C ++ 11 les dio std :: move (), lo que dejó en claro su intención para todos los pasantes que leerán y mantendrán el código).

Ajeet Ganga
fuente
1
Dado que nunca desea dos auto_ptrvalores que apunten al mismo objeto (dado que no dan propiedad compartida, el primero en morir dejará al otro con una herencia letal; esto también es cierto para el unique_ptruso), ¿puede sugerir lo que se pretendía en los restantes 96.8584073465% de todo el uso?
Marc van Leeuwen
No puedo hablar por todos ellos, pero supongo que pensarían que la propiedad del objeto se está moviendo y NO solo se está duplicando, lo cual es erróneo.
Ajeet Ganga
@AjeetGanga En la siguiente frase 'el pequeño- * (juego de palabras intencionado), mencionaste como "juego de palabras intencionado". Esta frase es nueva para mí y de todos modos la busqué en Google y supe que hay una broma que se hizo a propósito aquí. ¿Cuál es ese chiste de aquí? Solo curiosidad por saber eso.
VINOTH ENERGETIC
@AjeetGanga Mencionaste como 'la cláusula little- * (juego de palabras intencionado) del 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'. Digamos que tengo dos ptr automáticos como ayb para un entero. Estoy haciendo la asignación ya que *a=*b;aquí solo el valor de b se copia en a. Espero que la propiedad de a y b siga perteneciendo a las mismas personas. Mencionaste que la propiedad será transferida. ¿Cómo sera?
VINOTH ENERGETIC
@VINOTHENERGETIC Ajeet estaba hablando de asignar a un auto_ptrobjeto en sí. La asignación a / desde su valor señalado no tiene efecto ni relevancia para la propiedad. Espero que no se sigue usando auto_ptr?
underscore_d
23

shared_ptrse puede almacenar dentro de contenedores. auto_ptrhipocresía.

Por cierto, unique_ptres realmente el auto_ptrreemplazo directo , combina las mejores características de ambos std::auto_ptry boost::scoped_ptr.

Ben Voigt
fuente
11

Otra versión más para explicar la diferencia ...

Funcionalmente, C ++ 11 std::unique_ptres el "fijo" std::auto_ptr: ambos son adecuados cuando, en cualquier momento durante la ejecución, debería haber un único propietario de puntero inteligente para un objeto apuntado.

La diferencia crucial está en la construcción de copias o la asignación de otro puntero inteligente que no expira, que se muestra en las =>líneas siguientes:

   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, ap3silenciosamente "roba" la propiedad de *ap, dejándolo apconfigurado en a nullptr, y el problema es que puede suceder con demasiada facilidad, sin que el programador haya pensado en su seguridad.

Por ejemplo, si un class/ structtiene un std::auto_ptrmiembro, al hacer una copia de una instancia aparecerá releaseel puntero de la instancia que se está copiando: esa es una semántica extraña y peligrosamente confusa, ya que normalmente copiar algo no lo modifica. Es fácil para el autor de la clase / estructura pasar por alto la liberación del puntero cuando razona sobre invariantes y estados y, en consecuencia, intenta accidentalmente desreferenciar el puntero inteligente mientras es nulo, o simplemente no tiene el acceso / propiedad esperado de los datos apuntados.

Tony Delroy
fuente
auto_ptr silenciosamente "roba" la propiedad +1
camino
3

auto_ptr no se puede usar en contenedores STL porque tiene un constructor de copia que no cumple con los requisitos del contenedor CopyConstructible . unique_ptr no implementa un constructor de copia, por lo que los contenedores usan métodos alternativos. unique_ptr se puede usar en contenedores y es más rápido para los algoritmos estándar 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
edW
fuente