2009-04-24 10 views
6

Encontré este tipo de código de vez en cuando. Sospecho que el creador tiene/tenía miedo de que la tabla eliminada iterara sobre la tabla y el "rendimiento del costo" (que no se haga de ninguna manera) ... ¿hay algún beneficio real que uno pueda obtener/considerar/imaginar al no usar la tabla de borrar aquí?¿Por qué escribirías algo como esto? (intencionalmente no se usa delete [] en una matriz)

myClass** table = new myClass* [size]; 
... //some code that does not reallocate or change the value of the table pointer ;) 
delete table; // no [] intentionally 

Respuesta

3

Realmente no hay razón para escribir así y una razón seria para no hacerlo.

Es cierto que para los tipos con los destructores triviales (como punteros primas en su caso) que no hay necesidad de conocer el número real de elementos de la matriz y por lo que el compilador puede decidir asignar nueva [] y eliminar [ ] en nuevo y borre para reducir los gastos generales. Si decide de esta manera, no puede detenerlo sin tomar medidas adicionales, por lo que esta optimización del compilador se llevará a cabo sin previo aviso y será gratuita.

Al mismo tiempo alguien usando su código podría desear sobrecargar los operadores globales nueva y eliminar (y nueva [] y delete [] también). Si eso ocurre, se encontrará con un gran problema porque es cuando realmente puede necesitar la diferencia entre eliminar y eliminar [].

Agregue a esto que esta optimización dependiente del compilador no es posible de importar.

Así que este es el caso cuando no se obtienen beneficios desplazando delete [] con eliminar el riesgo, pero a lo grande basándose en un comportamiento indefinido.

+0

Gracias por resumir el hilo muy bien :) – RnR

+0

Como nota adicional, si uno realmente quiere realmente hacerlo No tener esa sobrecarga, un enfoque portátil sería usar malloc() y free() en lugar de new [] y eliminar [] – bdonlan

8

Es una pérdida de memoria. Un new [] debe coincidir con un delete []. Además, dado que table es un puntero al primer elemento de una matriz de punteros, cualquier miembro de la matriz, si es una matriz en sí misma, deberá ser desasignada utilizando un delete [].

+0

desafortunadamente no hay pérdida de memoria - para los tipos incorporados la tabla nueva y elimine prácticamente mapear a malloc y llamadas gratuitas (no hay diferencia si usa la tabla borrar o no porque los desctructores no necesitan ser llamados de ninguna manera) - Digo, lamentablemente, ya que preferiría que este tipo de hacks no funcionen en absoluto :) – RnR

+3

@RnR: Eso depende completamente de la implementación ... y ciertamente no siempre es el caso. – SoapBox

+0

¿De dónde vienen los tipos incorporados? Tu ejemplo consiste en myClass. Y sí, estás invocando a UB como lo menciona Neil. – dirkgently

19

Si hace esto, obtendrá lo que el estándar C++ llama comportamiento indefinido: cualquier cosa podría pasar.

1

esa declaración dejará a todos los objetos myClass señalados por todos los punteros en la matriz que quedan en la memoria. También deja la matriz de punteros en la memoria. No hay forma de que pueda ser útil, ya que solo libera hasta 32 bits de memoria, y el sistema operativo aún cree que tiene (tamaño) myClasses y punteros para cada uno en uso. Este es solo un ejemplo de un programador que no limpia correctamente después de sí mismo.

+1

Cabe señalar que una eliminación [] todavía va a dejar todos los objetos myClass colgando porque eliminar [] de la tabla simplemente se deshace de los indicadores. El programador todavía tiene que eliminar las cosas, señaladas por sí mismo. –

8

No solo no hay beneficio, el código es simplemente incorrecto; en el mejor de los casos, pierde memoria y, en el peor de los casos, puede bloquear su programa o abrir un agujero de seguridad difícil de encontrar. Siempre debe coincidir con new con delete y new[] con delete[]. Siempre.

5

Definitivamente es incorrecto ya que una s nueva [] debe emparejarse con delete []. Si no lo haces, obtendrás un comportamiento indefinido.

Puede funcionar (parcialmente), porque la mayoría de las implementaciones usan las nuevas para implementar las nuevas []. La única diferencia para dicha implementación sería que solo llamaría a 1 destructor (para el primer elemento en lugar de todos los destructores. Pero evítelo tal como es no legal C++.

2

En teoría, debe llamar a eliminar [] .

EDIT:. El siguiente se aplica sólo a Microsoft Visual C++ (que debería haber dicho esto)

en la práctica, en Microsoft Visual C++, no importa lo que elimina utiliza cuando los objetos en la matriz no tiene destructores. Como tiene una matriz de punteros y los punteros no pueden tener destructores, debería estar bien.

Sin embargo, como han señalado otros, es incorrecto C++ mezclar nuevo [] y eliminar sin []. Aunque puede funcionar en Visual C++ en este caso, el código no es portátil y puede fallar en otros compiladores.

Pero volviendo al caso específico de Visual C++, incluso si llama a delete [], el compilador se dará cuenta de que no necesita iterar a través de la matriz llamando a destructores cuando se trata de una matriz de tipos primitivos como int, char, o punteros. Llamar a eliminar en ese caso realmente funciona y no romperá nada. No sería más lento hacer lo correcto y llamar a eliminar [], pero tampoco será más rápido.

De hecho, en MSVC++, eliminar [] p llama inmediatamente al operador normal delete (void * p) cuando p es un puntero a un tipo simple, o uno sin destructores.

Aquellos que no me crean, recorran este código en el código CRT para las primeras dos llamadas para eliminar [].

#include "stdafx.h" 
#include <malloc.h> 
#include <iostream> 
using namespace std; 

class NoDestructor 
{ 
    int m_i; 
}; 

class WithDestructor 
{ 
public: 
    ~WithDestructor() 
    { 
     cout << "deleted one WithDestructor at " << (void *) this<< endl; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int **p = new int *[20]; 
    delete [] p; 

    p = (int**) malloc(80); 
    free(p); 

    NoDestructor *pa = new NoDestructor[20]; 
    delete [] pa; 

    WithDestructor *pb = new WithDestructor[20]; 
    delete [] pb; 

    return 0; 
} 
+1

Realmente no importa si te creemos. Estoy seguro de que en ese compilador en particular es el caso. El verdadero problema es que no * tiene * que sea el caso. MS puede cambiar ese comportamiento en cualquier momento. usar delete en lugar de delete [] en algo que se asignó con new [] es simplemente incorrecto. –

+0

Bien, no estoy abogando por la mezcla de los dos. No deberías mezclarlos. Solo estoy respondiendo la pregunta original sobre si tendría algún efecto negativo como pérdidas de memoria o golpes de rendimiento. En la práctica, en MSVC++, para este tipo de matriz, no tiene ningún efecto negativo. –

+0

@Thinkcube: tiene razón en que está respondiendo la pregunta correcta y sí, como podemos ver, no hay "ninguna diferencia" en el rendimiento en este caso, pero el código funciona "por accidente". Gracias – RnR

1

Consulte con la sección [16.11] "¿Cómo puedo asignar/unallocate una serie de cosas?" y más allá en C++ FAQ Lite,

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.11

Explican que la matriz eliminar es una necesidad cuando se crea una matriz.

La instancia de myClass señalada por los elementos de su matriz también debe eliminarse donde se crean.

+0

Diría que el punto 16.13 es incluso más pertinente, y parece que podría haber un caso (probablemente no este) en el que tal "falta intencional" de [] podría ser "necesaria" - sería si alguien lo hiciera no anular los operadores nuevos y eliminarlos correctamente en primer lugar y se preguntó por qué no se llama o cómo se llama;) – RnR

Cuestiones relacionadas