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 connew[]
, y tiene el eliminador predeterminadodefault_delete<Type[]>
, especializado para llamar adelete[] _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. Vershared_array
para ese uso.
Si desea administrar la memoria para una matriz de puntero, tiene algunas opciones que dependen de su requerimiento:
- Utilice
boost::shared_array
- Usar
std::vector
deboost::shared_ptr
- Utilice el contenedor de indicador de
boost::ptr_vector
comoboost::ptr_vector