smart shared_ptr pointer example c++ c++11 shared-ptr smart-pointers delete-operator

c++ - shared_ptr - ¿Cómo escogen los punteros inteligentes entre eliminar y eliminar[]?



smart pointer c++ (4)

De la documentación de Microsoft :

(Una especialización parcial unique_ptr<Type[]> administra objetos de matriz asignados con new[] , y tiene el eliminador predeterminado default_delete<Type[]> , especializado para llamar a delete[] _Ptr .)

Agregué los dos corchetes finales, parece un error tipográfico, ya que no tiene sentido sin ellos.

Considerar:

delete new std :: string [2]; delete [] new std :: string;

Todos saben que el primero es un error. Si el segundo no fuera un error, no necesitaríamos dos operadores distintos.

Ahora considera:

std :: unique_ptr <int> x (new int [2]); std :: unique_ptr <int> y (new int);

¿Sabe x usar delete[] en lugar de delete ?

Trasfondo: esta pregunta flotó en mi cabeza cuando pensé que la calificación de punteros tipo matriz sería una característica útil del lenguaje.

int *[] foo = new int [2]; // OK int * bar = new int; // OK delete [] foo; // OK delete bar; // OK foo = new int; // Compile error bar = new int[2]; // Compile error delete foo; // Compile error delete [] bar; // Compile error


Lamentablemente, no saben qué eliminar usar, por lo tanto, usan delete . Es por eso que para cada puntero inteligente tenemos una contraparte de matriz inteligente.

std::shared_ptr uses delete std::shared_array uses delete[]

Entonces, tu línea

std :: unique_ptr <int> x (new int [2]);

en realidad causa un comportamiento indefinido.

Por cierto, si escribes

std :: unique_ptr<int[]> p(new int[2]); ^^

luego se usará delete[] ya que lo solicitó explícitamente. Sin embargo, la siguiente línea seguirá siendo UB.

std :: unique_ptr<int[]> p(new int);

La razón por la que no pueden elegir entre delete y delete[] es que new int y new int[2] son exactamente del mismo tipo - int* .

Here pregunta relacionada con el uso de eliminadores correctos en caso de smart_ptr<void> y smart_ptr<Base> cuando Base no tiene destructor virtual.


No hay una forma "mágica" de detectar si un int* refiere a:

  • un único número asignado de un montón
  • una matriz asignada de montón
  • un entero en una matriz asignada de montón

El sistema de tipo perdió la información y ningún método de tiempo de ejecución (portable) puede arreglarlo. Es exasperante y un defecto de diseño serio (*) en C que C ++ heredó (por razones de compatibilidad, algunos dicen).

Sin embargo, hay algunas formas de tratar con matrices en punteros inteligentes.

En primer lugar, su tipo unique_ptr es incorrecto para tratar con una matriz, debe usar:

std::unique_ptr<int[]> p(new int[10]);

que debe llamar a delete[] . Sé que se habla de implementar una advertencia específica en Clang para detectar desajustes obvios con unique_ptr : es una cuestión de calidad de implementación (el estándar simplemente dice que es UB), y no todos los casos se pueden cubrir sin WPA.

En segundo lugar, un boost::shared_ptr puede tener un eliminador personalizado que podría, si lo diseña, llamar al operador delete[] correcto. Sin embargo, hay un boost::shared_array especialmente diseñado para esto. Una vez más, la detección de desajustes es una cuestión de calidad de implementación. std::shared_ptr sufre el mismo problema ( editado después de la observación de ildjarn ).

Estoy de acuerdo en que no es bonito. Parece tan detestable que un defecto de diseño (*) de los orígenes de C nos atormenta aún hoy.

(*) algunos dirán que C se inclina fuertemente hacia evitar gastos generales y esto habría agregado una sobrecarga. En parte estoy en desacuerdo: malloc siempre sabe el tamaño del bloque, después de todo.


std::unique_ptr no es para array, ya que cito el último documento de boost:

Normalmente, un shared_ptr no puede contener correctamente un puntero a una matriz dinámicamente asignada. Ver shared_array para ese uso.

Si desea administrar la memoria para una matriz de puntero, tiene algunas opciones que dependen de su requerimiento:

  1. Utilice boost::shared_array
  2. Usar std::vector de boost::shared_ptr
  3. Utilice el contenedor de indicador de boost::ptr_vector como boost::ptr_vector