¿Por qué no eliminar destruir nada?

Estoy jugando un poco con la asignación dinámica de memoria, pero no entiendo un punto. Al asignar memoria con la new instrucción, se supone que puedo destruir la memoria a la que apunta el puntero usando delete .

Pero cuando bash, este comando de delete no parece funcionar, ya que el espacio al que apunta el puntero no parece haberse vaciado.

Tomemos esta pieza de código verdaderamente básica como un ejemplo:

 #include  using namespace std; int main() { //I create a pointer-to-integer pTest, make it point to some new space, // and fulfill this free space with a number; int* pTest; pTest = new int; *(pTest) = 3; cout << *(pTest) << endl; // things are working well so far. Let's destroy this // dynamically allocated space! delete pTest; //OK, now I guess the data pTest pointed to has been destroyed cout << *(pTest) << endl; // Oh... Well, I was mistaking. return 0; } 

Cualquier pista ?

Es hora de aprender qué es un comportamiento indefinido. 🙂

En C ++, cuando haces algo ilegal / absurdo / malo / etc. el estándar a menudo dice que “conduce a un comportamiento indefinido”. Esto significa que desde ese momento en adelante, el estado de su progtwig no está garantizado por completo, y cualquier cosa podría suceder.

En el momento en que haces tu último *(pTest) , obtienes un comportamiento indefinido. Esto se debe a que pTest no apunta a un objeto válido, y la desreferenciación de dicho puntero no está definida. Entonces, lo que estás viendo está totalmente permitido: producción no definida.

Todo lo que hizo fue dicho “He terminado con esta asignación”. Una vez que haya dicho eso, no debería (y de hecho, no puede) inspeccionar ni preocuparse por ese recuerdo por más tiempo. Ni siquiera tiene sentido conceptual desasignar algo y luego tratar de usarlo; ¡Has dicho que terminaste!

Sin embargo, su resultado es algo predecible: es probable que su sistema operativo simplemente diga “está bien, gracias por la memoria” y eso es todo. No tiene ninguna razón para “restablecer” la memoria, o hacer algo especial. Eso sería una pérdida de tiempo, cuando nadie (incluido su propio progtwig) no lo está usando.

Pero recuerde, este resultado está completamente indefinido. No intente usar objetos que no existen. Tal vez una mejor prueba habría sido:

 #include  struct foo { ~foo() { std::cout << "foo is gone :(" << std::endl; } }; int main(void) { foo* f = new foo(); delete f; // you'll see that the object is destroyed. } 

Aunque parece que estabas buscando ver qué pasa con la memoria en sí misma. Solo recuerde que no tiene sentido deshacerse de la memoria y luego tratar de usarla, por lo que la respuesta es: quién sabe. Depende de su plataforma específica, a C ++ no le importa.

Llamar eliminar marcará el área de memoria como libre. No será necesario restablecer su valor anterior.

Se recomienda configurar su puntero a 0, después de llamar a eliminar:

 delete pTest; pTest = 0; 

El operador delete llama al destructor del objeto y desasigna la memoria previamente asignada al objeto. No afecta la variable del puntero que apunta al objeto eliminado.

Por lo tanto, al desreferenciar un puntero que apunta a un objeto destruido, obtendrá problemas.

La respuesta es el rendimiento .

Es una gran ayuda para la depuración llenar toda la memoria liberada con un valor no válido ( 0xCCCCCCCC , 0xDEADDEAD , etc.) para detectar bashs de usar punteros obsoletos en la memoria ya liberada.

Pero modificar una memoria liberada cuesta tiempo de CPU, por lo que por razones de rendimiento, el sistema operativo simplemente agregará el bloque de memoria liberado a su lista “libre” y dejará el contenido intacto.

La desreferenciación de un puntero que apunta a la memoria desasignada es un comportamiento indefinido.

Muchas veces solo funcionará, porque la memoria proporcionada por los new suele ser parte de una porción más grande de memoria asignada que administra el asignador. Cuando llame a delete , llamará a los destructores correspondientes y marcará la memoria como libre, lo que generalmente significa “listo para ser reutilizado”. Por lo tanto, al buscar en esa memoria encontrará los mismos datos que estaban allí antes de la llamada para delete , o algún otro dato si ese trozo de memoria ha sido reasignado después de una new llamada.

Tenga en cuenta que nada prohíbe que el asignador new / delete funcione como una envoltura delgada alrededor de las funciones de memoria virtual del sistema operativo, por lo que cuando todos los bloques asignados relativos a una página se han desasignado, se libera toda la página y cualquier bash de acceder violación de la dirección.

TL, versión DR: no deferencia los punteros que apuntan a la memoria desasignada: puede funcionar a veces, a veces le devolverá basura, a veces activará una violación de acceso.

Una buena forma de darse cuenta inmediatamente si está cometiendo este tipo de error es poner sus punteros en NULL después de borrar la memoria a la que apuntan: si su código intenta desreferenciar un puntero NULL, en casi cualquier sistema esto hará que la aplicación falle. , por lo que las fallas como estas no pasarán desapercibidas.

¿Qué significaría destruir los datos? Supongo que podría ponerlo a cero, pero ¿para qué molestarse? Se supone sucio cuando lo sacamos del medio ambiente, entonces, ¿por qué limpiarlo antes de devolverlo? No nos importa lo que contenga, porque estamos renunciando a nuestro derecho a leerlo. Y en cuanto a por qué eliminar no pone a cero el puntero en sí:

http://www2.research.att.com/~bs/bs_faq2.html#delete-zero

Solo un ejemplo simple para ilustrar lo que podría pasar, y qué significa el comportamiento indefinido que algunas personas mencionaron.

Si agregamos dos líneas adicionales de código antes de la impresión:

 delete pTest; int *foo = new int; *foo = 42; cout << *pTest << endl; 

El valor impreso de pTest podría ser 3, como lo fue en su caso. Sin embargo, el valor impreso también podría ser 42. Cuando se eliminó el puntero pTest, se liberó su memoria. Debido a esto, es posible que el puntero foo apunte a la misma ubicación en la memoria a la que pTest solía apuntar antes de eliminarse.

Podría haberse referido a cualquier parte de la memoria mapeada. O tal vez memoria no asignada, dependiendo de cuánto tiempo haya estado en ejecución su progtwig, detalles de las asignaciones de memoria y si las bibliotecas devuelven memoria al sistema operativo más adelante …

Si el delete borró toda la memoria que se está eliminando, los progtwigs tardarían mucho más tiempo en ejecutarse, ya que perderían mucho tiempo restregando la memoria que, de todos modos, se sobrescribirá tarde o temprano. Podría ser bueno para la depuración, pero en el uso de producción, simplemente no hay mucha necesidad de depurar los contenidos de la memoria. (Claves de cifrado son una buena excepción, por supuesto, delete antes de llamar delete o free es una buena idea).