2009-03-18 8 views

Respuesta

14

Los objetos creados con new[] deben usar delete[]. El uso de delete no está definido en las matrices.

Con malloc y libre tiene una situación más simple. Solo hay 1 función que libera los datos que usted asigna, no existe el concepto de que se llame a un destructor tampoco. La confusión simplemente viene porque delete[] y eliminar parecen similares. En realidad son 2 funciones completamente diferentes.

El uso de eliminar no llamará a la función correcta para eliminar la memoria. Debe llamar al delete[](void*), pero en su lugar llama al delete(void*). Por esta razón no se puede confiar en el uso de memoria asignada delete con new[]

See this C++ FAQ

[16.13] ¿Puedo dejar la [] cuando gama deleteing de algún tipo incorporado (char, int , etc.)?

No!

A veces los programadores piensan que la [] en el delete[] p sólo existe por lo el compilador llamará a los correspondientes destructores para todos los elementos de la matriz . Debido a este razonamiento, ellos suponen que una matriz de algún tipo incorporado como char o int puede ser delete d sin el []. Por ejemplo, que asumen el siguiente código válido es:

void userCode(int n) { 
    char* p = new char[n]; 
    ... 
    delete p; // ← ERROR! Should be delete[] p ! 
} 

Pero el código anterior es incorrecto, y que pueden causar un desastre en tiempo de ejecución. En particular, el código que se llama para delete p es operator delete(void*), pero el código que se llama para delete[] p es operator delete[](void*). El comportamiento predeterminado para este último es llamar a la antigua, pero los usuarios se les permite reemplazar el último con un comportamiento diferente (en cuyo caso que normalmente también reemplazar el código correspondiente en la nueva operador new[](size_t)). Si reemplazados el código delete[] por lo que no era compatible con el código de eliminación , y que llaman el equivocado (es decir, si usted dijo delete p en lugar de delete[] p), que podría terminar con un desastre en tiempo de ejecución .

¿Por qué existe delete[] en primer lugar?

Si lo hace xoy:

char * x = new char[100]; 
char * y = new char; 

Ambos se almacenan en las variables char * mecanografiadas.

Creo que el motivo de la decisión de delete, y delete[] va junto con una larga lista de decisiones que están a favor de la eficiencia en C++. Es así que no hay un precio forzoso para hacer una búsqueda de cuánto se debe eliminar para una operación de eliminación normal.

Tener 2 new y new[] parece lógico tener delete y delete[] de todos modos por simetría.

+0

quizás podría agregar a la respuesta la información/enlaces al anular varios nuevos/eliminar para responder la pregunta posterior. –

+0

@Brian Por curiosidad: ¿por qué la necesidad de distinguir entre los dos? Como en, ¿por qué la eliminación [] existe en primer lugar? – FreeMemory

11

La diferencia es que delete solo borrará todo el rango de memoria, pero solo llamará al destructor por 1 objeto. delete[] eliminará la memoria y llamará al destructor para cada objeto. Si no usa delete[] para matrices, es solo cuestión de tiempo antes de que introduzca una fuga de recursos en su aplicación.

EDITAR actualización

Según el estándar, pasando un objeto asignado con new[]-delete es indefinido. El probable comportamiento es que va a actuar como lo describí.

+0

Entonces, ¿por qué es una buena práctica eliminar [] las matrices de caracteres entonces? ¿Solo para mantener el hábito? –

+0

Ummm, estoy bastante seguro de que invocar eliminar en el resultado de new [] (o viceversa) es un comportamiento indefinido. Por lo tanto, puede llevar a cosas mucho peores que la filtración de memoria. – derobert

+0

@derobert, no estoy 100% seguro si está definido o no, pero ciertamente crea un mal comportamiento. – JaredPar

-1

Cuando utiliza nuevo [] para asignar una matriz, en realidad le dice a C++ el tamaño de la matriz. Cuando utiliza malloc, en cambio le indica cuánta memoria está asignada. En el primer caso, la liberación basada en el tamaño de la matriz no tendría sentido. En este caso, lo hace. Pero dado que no hay diferencia entre un puntero para una matriz y para un solo objeto, se necesita una función separada.

0

new y delete son diferentes de malloc y libres en ese malloc y solo asignan y liberan libremente la memoria; ellos no llaman ctors o dtors.

3

El motivo de este requisito es histórico y porque new type y new type [size] devuelven cosas diferentes que deben limpiarse de forma diferente.

consideran este código

Foo* oneEntry = new Foo; 
Foo* tenEntries = new Foo[10]; 

Estos ambos devuelven un puntero Foo *, la diferencia es la segunda llamada se traducirá en el constructor de Foo se llama 10 veces, y estar allí más o menos 10 veces tanta memoria.

Así que ahora quiere liberar sus objetos.

Para un solo objeto que llamaría eliminar - p. delete oneEntry. Esto llama al destructor de objetos y desasigna la memoria.

Pero aquí está el problema: oneEntry y tenEntries son ambos solo punteros de Foo. El compilador no tiene idea de si apuntan a uno, diez o mil elementos.

Cuando utiliza la sintaxis especial de delete [].Esto le dice al compilador que "se trata de una matriz de objetos, calcula el recuento y luego destrúyelos a todos".

Lo que realmente sucede es que para el compilador new type [size] almacena en secreto 'tamaño' en otro lugar. Cuando llama a delete [], sabe que este valor secreto existe para que pueda descubrir cuántos objetos hay en ese bloque de memoria y destruirlos.

La pregunta que podría hacer es "¿por qué el compilador no almacena siempre el tamaño?"

Esa es una gran pregunta y se remonta a los primeros días de C++. Hubo un deseo de que para los tipos incorporados (char, int, float, etc.) lo siguiente sería válido para C++;

int* ptr = new int; 
free(ptr); 

int* ptr = (int*)malloc(sizeof(int) * someSize); 
delete ptr; 

El razonamiento detrás de esto fue la expectativa de que las personas proporcionarían bibliotecas que volvieron asignados dinámicamente la memoria, y los usuarios de estas bibliotecas no tendría forma de saber si se debe utilizar libre/borrar.

Este deseo de compatibilidad significaba que el tamaño de una matriz no se podía almacenar como parte de la matriz en sí y tenía que guardarse en otra parte. Debido a esta sobrecarga (y recuerde, esto fue a principios de los 80), se decidió hacer este libro solo para arreglos y no para elementos individuales. Por lo tanto, las matrices necesitan una sintaxis de eliminación especial que busque este valor.

La razón por la que malloc/free no tiene este problema es que simplemente se ocupan de los bloques de memoria y no tienen que preocuparse por llamar a los constructores/destructores.

1

En cuanto al "por qué" en el título: uno de los objetivos de diseño de C++ era que no habría ningún costo oculto. C++ también se desarrolló en un momento en que cada byte de memoria todavía importaba mucho más de lo que lo hace hoy en día. A los diseñadores de idiomas también les gusta la ortogonalidad: si asigna la memoria con new[] (en lugar de new), debe liberarla con delete[].

No creo que haya ninguna técnica razón por la que new[] no podían pegarse un "yo soy una matriz" indicador en la cabecera del bloque de memoria para delete (no más delete[]) para mirar adelante.

4

BS habla de las razones de por separado new/new[] y delete/ delete [] `operadores en "El diseño y evolución de C++" en las secciones 10.3 a 10.5.1:

  • 10,3 Asignación de matriz - discute que querían una forma de permitir que las matrices de objetos se asignaran usando un esquema separado de la asignación de objetos individuales (es decir, asignando matrices desde un almacén separado). Agregar las versiones de matriz de new y delete fue una solución para esto;
  • 10.5.1 desasignar matrices - analiza cómo un problema con las matrices desasignar utilizando sólo un único operador delete es que es necesario que haya más información que sólo el puntero con el fin de determinar si el puntero al primer elemento de una array o si solo apunta a un solo objeto. En lugar de "complicar el caso común de asignación y desasignación de objetos individuales", el operador delete[] se utiliza para gestionar matrices. Esto encaja con la filosofía general de diseño de C++ de "no pague por lo que no usa".

Si esta decisión fue un error o no es discutible, de cualquier manera tiene buenos argumentos, pero tenemos lo que tenemos.

Cuestiones relacionadas