¿Eliminar es igual a eliminar?

IP_ADAPTER_INFO *ptr=new IP_ADAPTER_INFO[100]; 

si libero usando

 delete ptr; 

conducirá a la pérdida de memoria, si no, ¿por qué?

Este es el código de desensamblaje generado por VS2005

 ; delete ptr; 0041351D mov eax,dword ptr [ptr] 00413520 mov dword ptr [ebp-0ECh],eax 00413526 mov ecx,dword ptr [ebp-0ECh] 0041352C push ecx 0041352D call operator delete (4111DBh) 00413532 add esp,4 ; delete []ptr; 00413535 mov eax,dword ptr [ptr] 00413538 mov dword ptr [ebp-0E0h],eax 0041353E mov ecx,dword ptr [ebp-0E0h] 00413544 push ecx 00413545 call operator delete[] (4111E5h) 0041354A add esp,4 

Si esto lleva a una pérdida de memoria, limpia su disco duro, lo deja embarazada, hace que Nasty Demons lo persiga por su apartamento, o deja que todo funcione bien sin problemas aparentes, no está definido. Puede ser de esta manera con un comstackdor, y cambiar con otro, cambiar con una nueva versión del comstackdor, con cada nueva comstackción, con las fases lunares, su estado de ánimo o dependiendo de la cantidad de neutrinos que pasaron por el procesador en el último día soleado tarde. O tal vez no.

Todo eso, y un número infinito de otras posibilidades se ponen en un término: comportamiento indefinido :

Solo aléjate de eso.

Solo una ilustración de algunos comportamientos “indefinidos” en ciertos sistemas operativos y comstackdores. Espero que pueda ser útil para las personas depurar su código.

Prueba 1

 #include  using namespace std; int main() { int *p = new int[5]; cout < < "pass" << endl; delete p; return 0; } 

Prueba 2

 #include  using namespace std; int main() { int *p = new int; cout < < "pass" << endl; delete[] p; return 0; } 

Prueba 3

 #include  using namespace std; struct C { C() { cout < < "construct" << endl; } ~C() { cout << "destroy" << endl; } }; int main() { C *p = new C[5]; cout << "pass" << endl; delete p; return 0; } 

Prueba 4

 #include  using namespace std; struct C { C() { cout < < "construct" << endl; } ~C() { cout << "destroy" << endl; } }; int main() { C *p = new C; cout << "pass" << endl; delete[] p; return 0; } 
  • Windows 7 x86, msvc 2010. Compile con las opciones predeterminadas, es decir, el manejador de excepciones está habilitado.

Prueba 1

 pass 

Prueba 2

 pass 

Prueba 3

 construct construct construct construct construct pass destroy # Then, pop up crash msg 

Prueba 4

 construct pass destroy destroy destroy destroy destroy destroy destroy ... # It never stop until CTRL+C 
  • Mac OS X 10.8.5, llvm-gcc 4.2 o gcc-4.8 generan la misma salida

Prueba 1

 pass 

Prueba 2

 pass 

Prueba 3

 construct construct construct construct construct pass destroy a.out(71111) malloc: *** error for object 0x7f99c94000e8: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug zsh: abort ./a.out 

Prueba 4

 construct pass a.out(71035) malloc: *** error for object 0x7f83c14000d8: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug zsh: abort ./a.out 
  • Ubuntu 12.04, AMD64, gcc 4.7

Prueba 1

 pass 

Prueba 2

 pass 

Prueba 3

 construct construct construct construct construct *** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x0000000001f10018 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7fe81d878b96] ./a.out[0x400a5b] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7fe81d81b76d] ./a.out[0x4008d9] ======= Memory map: ======== .... zsh: abort (core dumped) ./a.out 

Prueba 4

 construct destroy destroy destroy destroy destroy destroy destroy destroy ... destroy destroy *** glibc detected *** ./a.out: free(): invalid pointer: 0x00000000016f6008 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7fa9001fab96] ./a.out[0x400a18] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7fa90019d76d] ./a.out[0x4008d9] ======= Memory map: ======== ... zsh: abort (core dumped) ./a.out 

Por lo general, no se filtrará porque en el caso de los destructores POD son triviales y no hay necesidad de invocarlos, así que delete simplemente desasigna la memoria ocupada por la matriz. La desasignación de memoria requiere solo un valor de puntero para que se devuelva al montón. El conjunto acciona un bloque contiguo de memoria y, por lo tanto, la desasignación puede ser exitosa, como si se tratara de una desasignación de un solo elemento.

Pero no confíe en esto ya que es un comportamiento indefinido. Tal vez funciona bien, tal vez sucede algo horrible, funciona en este comstackdor, no funciona en otro y muchas personas le agradecen que haya encontrado un error.

Vea esta respuesta para más detalles.

delete : llama al destructor apropiado solo para el elemento al que apunta (de ser necesario), luego libera el fragmento de memoria

delete [] : llama a los destructores apropiados para cada elemento en su matriz (si es necesario), luego libera el fragmento de memoria

Si A apunta a una matriz que se asignó mediante la nueva T [n], entonces debe eliminarla mediante eliminar [] A.

¿Por qué?

La diferencia entre eliminar y eliminar [] es directa: la primera destruye un objeto escalar y la segunda destruye una matriz.

Más información aquí y aquí .

ACTUALIZACIÓN 1 (incorrecto):

Si usa delete (y no elimina []) mientras asigna con T [n] nuevo, solo se libera el primer elemento mientras que otros no se destruyen, lo que provocará la pérdida de memoria . ¿Por qué? Esta es la forma en que Bjarne Stroustrup y otros diseñaron el lenguaje. Y no es variable en el comstackdor. Si un comstackdor desasigna el camino diferente, simplemente no sigue el estándar . El lenguaje de progtwigción C ++ , capítulos 6.2.6.2 y 19.4.5.

ACTUALIZACIÓN 2 (correcto):

Admitiré mi error sobre el comportamiento de usar el operador delete en asignaciones con la nueva T [n]. Al leer el documento mencionado, no encontré la descripción exacta, por lo que supongo que en esta situación el comportamiento será indefinido y variará de comstackdor a comstackdor. AFAIK, el comstackdor de MSVC, por ejemplo, producirá un código diferente al de GCC. Por favor ignore la ACTUALIZACIÓN 1 .

Para una matriz de POD, no tendrá fugas (con la mayoría de los comstackdores). Por ejemplo, MSVC genera un código idéntico para eliminar y eliminar [] para una matriz de POD .

Personalmente, creo que C / C ++ podría estar sin operador delete []. El comstackdor conoce el tamaño del objeto y el tamaño de la memoria asignada se conoce en el tiempo de ejecución, por lo tanto, es muy simple saber que es una matriz de puntero o no y disponer la memoria de una manera correcta.

EDITAR:

Ok muchachos. ¿Puedes probar en tu comstackdor y decir si tiene fugas?

Intenta pensar como un desarrollador de comstackdores. Tenemos nuevos , nuevos [] , eliminar , eliminar [] . Cada nuevo tiene su propia eliminación . Parece perfecto y completo. Veamos qué está pasando cuando llamas a delete [] ?

 1. call vector destructor for an object 2. actual free memory 

¿Qué es destructor para POD ? ¡Nada! Por lo tanto, ¡llamar a eliminar para una matriz de POD no tendrá fugas! Incluso si rompe el estándar. Incluso si no es recomendado

EDIT2:

Este es el código de desensamblaje generado por VS2008:

 operator delete[]: 78583BC3 mov edi,edi 78583BC5 push ebp 78583BC6 mov ebp,esp 78583BC8 pop ebp 78583BC9 jmp operator delete (78583BA3h)