Pregunta

Esta es una pregunta que me ha estado molestando durante algún tiempo. Siempre pensé que C ++ debería haber sido diseñado para que " eliminar " el operador (sin paréntesis) funciona incluso con el " nuevo [] " operador.

En mi opinión, escribiendo esto:

int* p = new int;

debería ser equivalente a asignar una matriz de 1 elemento:

int* p = new int[1];

Si esto fuera cierto, el " eliminar " el operador siempre podría eliminar matrices, y no necesitaríamos el " delete [] " operador.

¿Hay alguna razón por la que " eliminar [] " ¿El operador fue introducido en C ++? La única razón por la que puedo pensar es que la asignación de matrices tiene una pequeña huella de memoria (tiene que almacenar el tamaño de la matriz en algún lugar), por lo que distinguir " eliminar " vs " eliminar [] " Era una pequeña optimización de memoria.

¿Fue útil?

Solución

Es para que se llame a los destructores de los elementos individuales. Sí, para las matrices de POD, no hay mucha diferencia, pero en C ++, puedes tener matrices de objetos con destructores no triviales.

Ahora, tu pregunta es, ¿por qué no hacer que new y delete se comporten como new [] y delete [] y deshacerse de new [] y delete [] ? Volvería "Diseño y evolución" de Stroustrup libro donde dijo que si no usa las funciones de C ++, no debería tener que pagar por ellas (al menos en tiempo de ejecución). Tal como está ahora, un nuevo o delete se comportará tan eficientemente como malloc y free . Si delete tuviera el significado de delete [] , habría una sobrecarga adicional en el tiempo de ejecución (como lo señaló James Curran).

Otros consejos

Maldición, me he perdido todo el punto de la pregunta, pero dejaré mi respuesta original como una nota al margen. El motivo por el que eliminamos [] es porque hace mucho tiempo eliminamos [cnt], incluso si escribes delete [9] o delete [cnt], el compilador simplemente ignora el elemento entre [] pero compila ok. En ese momento, C ++ primero fue procesado por un front-end y luego alimentado a un compilador C ordinario. No podían hacer el truco de almacenar el conteo en algún lugar debajo de la cortina, tal vez ni siquiera podían pensar en eso en ese momento. Y para la compatibilidad con versiones anteriores, lo más probable es que los compiladores usaran el valor dado entre [] como el recuento de la matriz, si no hay tal valor, obtuvieron el recuento del prefijo, por lo que funcionó en ambos sentidos. Más tarde, no escribimos nada entre [] y todo funcionó. Hoy, no creo que " elimine [] " Es necesario pero las implementaciones así lo exigen.

Mi respuesta original (que pierde el punto) ::

" eliminar " borra un solo objeto. " eliminar [] " borra una matriz de objetos. Para que elimine [] para que funcione, la implementación mantiene la cantidad de elementos en la matriz. Acabo de verificar esto mediante la depuración del código ASM. En la implementación (VS2005) que probé, el recuento se almacenó como un prefijo de la matriz de objetos.

Si usas " eliminar [] " en un solo objeto, la variable de conteo es basura, por lo que el código se bloquea. Si utiliza " eliminar " para una matriz de objetos, debido a alguna inconsistencia, el código falla. ¡He probado estos casos justo ahora!

" eliminar simplemente elimina la memoria asignada para la matriz. " declaración en otra respuesta no es correcta. Si el objeto es una clase, eliminar llamará al DTOR. Simplemente coloque un punto de interrupción en el código DTOR y elimine el objeto, el punto de interrupción llegará.

Lo que se me ocurrió es que, si el compilador & amp; las bibliotecas asumieron que todos los objetos asignados por " nuevo " son matrices de objetos, sería correcto llamar a " eliminar " para objetos individuales o matrices de objetos. Los objetos individuales solo serían el caso especial de una matriz de objetos con un conteo de 1. Tal vez haya algo que me falta, de todos modos ...

Como todos los demás parecen haberse perdido el punto de su pregunta, simplemente agregaré que tuve el mismo pensamiento hace un año y que nunca he podido obtener una respuesta.

Lo único en lo que puedo pensar es que hay una pequeña sobrecarga adicional para tratar un solo objeto como una matriz (un " innecesario para (int i = 0; i < 1; ++ i ) ")

Agregando esto ya que ninguna otra respuesta actualmente lo aborda:

Array delete [] nunca se puede usar en una clase de puntero a base - mientras que el compilador almacena el conteo de objetos cuando invoca new [] , no almacena los tipos o tamaños de los objetos (como señaló David, en C ++, rara vez paga por una función que no está usando). Sin embargo, el delete escalar puede eliminarse de manera segura a través de la clase base, por lo que se usa tanto para la limpieza normal de objetos como para la limpieza polimórfica:

struct Base { virtual ~Base(); };
struct Derived : Base { };
int main(){
    Base* b = new Derived;
    delete b; // this is good

    Base* b = new Derived[2];
    delete[] b; // bad! undefined behavior
}

Sin embargo, en el caso contrario, el destructor no virtual, el delete escalar debería ser lo más barato posible, no debería verificar la cantidad de objetos ni el tipo de objeto eliminado Esto hace que la eliminación en un tipo incorporado o tipo de datos sin formato antiguo sea muy barata, ya que el compilador solo necesita invocar :: operator delete y nada más:

int main(){
    int * p = new int;
    delete p; // cheap operation, no dynamic dispatch, no conditional branching
}

Aunque no es un tratamiento exhaustivo de la asignación de memoria, espero que esto ayude a aclarar la amplitud de las opciones de administración de memoria disponibles en C ++.

Marshall Cline tiene algunos información sobre este tema .

delete [] garantiza que se llame al destructor de cada miembro (si corresponde al tipo), mientras que delete simplemente elimina la memoria asignada para la matriz.

Aquí hay una buena lectura: http: //www.informit. com / guides / content.aspx? g = cplusplus & amp; seqNum = 287

Y no, los tamaños de matriz no se almacenan en ningún lugar en C ++. (Gracias a todos por señalar que esta declaración es inexacta).

Estoy un poco confundido por la respuesta de Aaron y reconozco francamente que no entiendo completamente por qué y dónde se necesita eliminar [].

Hice algunos experimentos con su código de ejemplo (después de corregir algunos errores tipográficos). Aquí están mis resultados.     Typos: ~ Base necesitaba un cuerpo de función     La base * b fue declarada dos veces

struct Base { virtual ~Base(){ }>; };
struct Derived : Base { };
int main(){
Base* b = new Derived;
delete b; // this is good

<strike>Base</strike> b = new Derived[2];
delete[] b; // bad! undefined behavior
}

Compilación y ejecución

david@Godel:g++ -o atest atest.cpp 
david@Godel: ./atest 
david@Godel: # No error message

Programa modificado con delete [] eliminado

struct Base { virtual ~Base(){}; };
struct Derived : Base { };

int main(){
    Base* b = new Derived;
    delete b; // this is good

    b = new Derived[2];
    delete b; // bad! undefined behavior
}

Compilación y ejecución

david@Godel:g++ -o atest atest.cpp 
david@Godel: ./atest 
atest(30746) malloc: *** error for object 0x1099008c8: pointer being freed was n
ot allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

Por supuesto, no sé si delete [] b está funcionando realmente en el primer ejemplo; Solo sé que no da un mensaje de error del compilador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top