5

No entiendo como funciona el operador delete en esta función ya que std::cout << datosCli[0] << '\n';, que es ejecutado tras delete[] datosCli; sigue imprimiendo por pantalla el String. ¿No debería perdese la referencia al contenido del array tras llamar a delete[]?

string * Taquilla::genDatosCliente(){

 string nombres[] = {"Sergio","Ernesto","Julio","Fabio","María","Laura","Manolo","Luisa",
                 "Julia","Marta","Irene","Carlos","Pablo","Enrique","Esteban","Amador",
                 "Jose","Eva","Azucena","Samuel", "Gandalf"};

 string apellidos[] = {"Salaices","Oliva","Garcia","Fuentes","Merino","Robles","Lifton","Redondo",
                   "Rivas", "Fuerte","Muñoz","Blanco","Calvete","Moreno","Dominguez","Martinez",
                   "Ramos","Navarro","Prieto","Vidal","Pascual","Guerrero","Soto","Caballero",
                   "Gil","Yela","Riofrio","Otegi","Roman","Espejo"};

 char letraDNI[] = {'T','R','W','A','G','M','Y','F','P','D','X','B',
                 'N','J','Z','S','Q','V','H','L','C','K','E'};

 // Semilla del Random
 srand(time(NULL));

 // Obtención de los datos del cliente mediante los índices generados
 // aleatoriamente.
 string nombre = nombres[rand()%((sizeof(nombres))/(sizeof(nombres[0])))];
 string apellido1 = apellidos[rand() %((sizeof(apellidos))/(sizeof(apellidos[0])))];
 string apellido2 = apellidos[rand() %((sizeof(apellidos))/(sizeof(apellidos[0])))];

 string *datosCli = new string[4];

 datosCli[0] = nombre;
 datosCli[1] = apellido1;
 datosCli[2] = apellido2;

 int numDNI = 10000000 + rand() % (99999999-10000000);
 datosCli[3] = to_string(numDNI) + letraDNI[numDNI%23];

 delete[] datosCli;
 std::cout << datosCli[0] << '\n';

 return datosCli;
}
eferion
  • 49,291
  • 5
  • 30
  • 72

2 Answers2

7

Usar un objeto después de haberle hecho delete es un comportamiento indefinido (en inglés, undefined behavior, muchas veces escrito como UB).

¿Y qué pasa con el comportamiento indefinido? Pues como su propio nombre indica, pues no está definido.

Igual el programa funciona tal como esperas. Igual el programa aborta y finaliza, con un error. Igual le envía un email a tu jefe. En tres palabras:

NO. ESTÁ. DEFINIDO.

No puedes esperar que el comportamiento observado se mantenga constante si cambias el compilador, o tocas una sola línea de código del programa, incluso en diferentes ejecuciones.

En concreto, el delete lo único que tiene que hacer es llamar al destructor del objeto y marcar la memoria que éste ocupaba como "libre". No tiene porqué poner esa memoria a cero, ni hacer que la variable apunte a otra posición de memoria.

Como los programadores somos gente aburrida a los que nos gusta que los programas se comporten como esperamos sin sorpresas, evita UBs aunque parezca que "funciona".

Más información sobre comportamiento indefinido, no especificado y definido por la implementación

SJuan76
  • 10,771
  • 5
  • 17
  • 31
2

Aunque no lo parezca, C++ no se encarga de la gestión de memoria, de eso se encarga el Sistema Operativo (SO). Lo que hace C++ es informar al SO de la memoria que necesita y también informa de cuándo ha dejado de usar dicha memoria; con esta información el SO (no el lenguaje de programación) gestiona la memoria.


Imagina que te vas de viaje, te alojas en un bonito hotel, antes de dormir lees un rato un libro y cuando te vence el sueño lo dejas en la mesilla de noche. Al día siguiente haces tus maletas y vas dirección a la estación de tu medio de transporte favorito ¡pero olvidas el libro!. ¡No pasa nada! ¡Aún estás a tiempo de volver y recuperarlo! Corres raudo al hotel, vuelves a tu habitación y encuentras el libro ahí ¡menos mal!

Pero ¡un momento! ¿No se supone que se limpian las habitaciones cuando el huésped las abandona? ¿Cómo es posible que el libro siga ahí cuando la habitación debería haberse limpiado?.

¿No debería perderse la referencia al contenido del array tras llamar a delete[]?

¿No debería perderse el contenido de la habitación de hotel tras hacer el checkout?

Pues no, el personal del hotel puede hacerse cargo de la habitación que estuviste usando cuando a ellos les venga bien; y esto no tiene por que ser después de tu checkout si no en cualquier otro momento.

Igual sucede con el SO y la gestión de memoria: el SO puede hacerse cargo de la memoria que estuviste usando cuando le venga bien; y esto no tiene por que ser después de tu delete[] si no en cualquier otro momento. Es más, el SO puede decidir marcar la memoria como disponible pero sin modificar su contenido; ¡de esta manera los datos antiguos estarían presentes aún después del borrado!.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82