Eliminar un puntero en C ++

92

Contexto: Estoy tratando de entender los consejos, los vimos hace un par de semanas en la escuela y mientras practicaba hoy me encontré con un tonto problema, puede ser muy sencillo para usted, pero tengo poca o ninguna experiencia en programación.

He visto bastantes preguntas en SO sobre la eliminación de punteros, pero todas parecen estar relacionadas con la eliminación de una clase y no con un puntero 'simple' (o cualquiera que sea el término correcto), aquí está el código que estoy tratando de correr:

#include <iostream>;

using namespace std;

int main() {
  int myVar,
      *myPointer;

  myVar = 8;
  myPointer = &myVar;

  cout << "delete-ing pointers " << endl;
  cout << "Memory address: " << myPointer << endl;

  // Seems I can't *just* delete it, as it triggers an error 
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // Error: a.out(14399) malloc: *** error for object 0x7fff61e537f4:
  // pointer being freed was not allocated
  // *** set a breakpoint in malloc_error_break to debug
  // Abort trap: 6

  // Using the new keyword befor deleting it works, but
  // does it really frees up the space? 
  myPointer = new int;
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // myPointer continues to store a memory address.

  // Using NULL before deleting it, seems to work. 
  myPointer = NULL;
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // myPointer returns 0.

}

Entonces mis preguntas son:

  1. ¿Por qué no funciona el primer caso? ¿Parece el uso más sencillo de usar y eliminar un puntero? El error dice que la memoria no se asignó pero 'cout' devolvió una dirección.
  2. En el segundo ejemplo, el error no se activa, pero al hacer un cout del valor de myPointer todavía se devuelve una dirección de memoria.
  3. ¿El # 3 realmente funciona? Me parece que funciona, el puntero ya no almacena una dirección, ¿es esta la forma correcta de eliminar un puntero?

Perdón por la pregunta larga, quería dejar esto lo más claro posible, también para reiterar, tengo poca experiencia en programación, así que si alguien pudiera responder esto usando términos sencillos, ¡sería muy apreciado!

leópico
fuente
16
La razón por la que no ve el primer ejemplo es porque está mal. Solo deletelo que tu new. Tampoco es necesario que el puntero se establezca en NULL después de eliminarlo. Si desea seguridad allí, use punteros inteligentes, que liberan la memoria para usted y dan errores cuando intenta acceder a ellos cuando no contienen algo.
chris
Hmm, está bien, no estoy seguro de qué son los consejos inteligentes, pero lo investigaré, ¡gracias!
leópico
1
En pocas palabras, hacen lo que describí. Para retener algo nuevo, llamas resety libera al viejo. Para liberarlo sin recambio, llama release. Cuando sale del alcance, se destruye y podría liberar la memoria según el tipo que sea. std::unique_ptrestá destinado a un solo propietario. std::shared_ptrlo libera cuando el último propietario deja de poseer el recurso. También son excepcionales a salvo. Si asigna un recurso con uno y luego encuentra una excepción, el recurso se liberará correctamente.
chris

Respuestas:

168

1 y 2

myVar = 8; //not dynamically allocated. Can't call delete on it.
myPointer = new int; //dynamically allocated, can call delete on it.

La primera variable se asignó a la pila. Puede llamar a eliminar solo en la memoria que asignó dinámicamente (en el montón) usando elnew operador.

3.

  myPointer = NULL;
  delete myPointer;

Lo anterior no hizo nada en absoluto . No liberaste nada, ya que el puntero apuntaba a NULL.


No se debe hacer lo siguiente:

myPointer = new int;
myPointer = NULL; //leaked memory, no pointer to above int
delete myPointer; //no point at all

Lo apuntó a NULL, dejando atrás la memoria filtrada (el nuevo int que asignó). Deberías liberar la memoria que estabas apuntando. Ya no hay forma de acceder a lo asignado new int, por lo tanto, pérdida de memoria.


La forma correcta:

myPointer = new int;
delete myPointer; //freed memory
myPointer = NULL; //pointed dangling ptr to NULL

La mejor forma:

Si está utilizando C ++, no utilice punteros sin formato. En su lugar, use punteros inteligentes que puedan manejar estas cosas por usted con poca sobrecarga. C ++ 11 viene con varios .

Anirudh Ramanathan
fuente
13
<pedantry> "En la pila" es un detalle de implementación, uno que C ++ evita mencionar visiblemente. El término más correcto es "con duración de almacenamiento automático". (C ++ 11, 3.7.3) </
pedantry
4
Gracias, seleccioné su respuesta para a) explicar lo que estaba mal yb) dar una mejor práctica, ¡muchas gracias!
leopic
6
@Tqn Eso no está bien. delete myPointerdesasigna *myPointer. Eso es correcto. Pero myPointercontinúa apuntando a una ubicación de memoria que se ha liberado y no debe usarse ya que es UB. Será inaccesible después del final del alcance solo si fue una variable local en primer lugar.
Anirudh Ramanathan
2
@DarkCthulhu ¡Gracias! (Literalmente) aprendo algo newtodos los días. (¡Soy cursi!)
Tqn
1
@AmelSalibasic La memoria asociada con la variable en la pila se liberará solo una vez que salga del alcance. Asignarlo a NULLevita que hagamos un mal uso de él más adelante.
Anirudh Ramanathan
24

Creo que no comprende completamente cómo funcionan los punteros.
Cuando tienes un puntero apuntando a una memoria, hay tres cosas diferentes que debes entender:
- hay "lo que apunta" el puntero (la memoria)
- esta dirección de memoria
- no todos los punteros necesitan borrar su memoria: solo necesita eliminar la memoria que se asignó dinámicamente ( newoperador usado ).

Imagina:

int *ptr = new int; 
// ptr has the address of the memory.
// at this point, the actual memory doesn't have anything.
*ptr = 8;
// you're assigning the integer 8 into that memory.
delete ptr;
// you are only deleting the memory.
// at this point the pointer still has the same memory address (as you could
//   notice from your 2nd test) but what inside that memory is gone!

Cuando lo hiciste

ptr = NULL;
// you didn't delete the memory
// you're only saying that this pointer is now pointing to "nowhere".
// the memory that was pointed by this pointer is now lost.

C ++ permite que intentes con deleteun puntero que apunta, nullpero en realidad no hace nada, simplemente no da ningún error.

salgadokk
fuente
2
Gracias, ESTO fue muy útil, pensé que TENÍA que borrar todos los punteros, no sabía que era solo para los que eran nuevos, gracias.
leópico
13

Los punteros son similares a las variables normales en que no es necesario eliminarlos. Se eliminan de la memoria al final de la ejecución de una función y / o al final del programa.

Sin embargo, puede usar punteros para asignar un 'bloque' de memoria, por ejemplo, como este:

int *some_integers = new int[20000]

Esto asignará espacio de memoria para 20000 enteros. Útil, porque la pila tiene un tamaño limitado y es posible que desee meterse con una gran cantidad de 'ints' sin un error de desbordamiento de pila.

Siempre que llame a new, debe 'eliminar' al final de su programa, porque de lo contrario obtendrá una pérdida de memoria y parte del espacio de memoria asignado nunca será devuelto para que lo usen otros programas. Para hacer esto:

delete [] some_integers;

Espero que ayude.

usuario3728501
fuente
1
Solo quiero agregar que la memoria asignada SERÁ devuelta para que la usen otros programas, pero solo DESPUÉS de que su programa haya terminado de ejecutarse.
sk4l
7

Hay una regla en C ++, por cada nuevo hay una eliminación .

  1. ¿Por qué no funciona el primer caso? ¿Parece el uso más sencillo de usar y eliminar un puntero? El error dice que la memoria no se asignó pero 'cout' devolvió una dirección.

nuevo nunca se llama. Entonces, la dirección que imprime cout es la dirección de la ubicación de memoria de myVar, o el valor asignado a myPointer en este caso. Por escrito:

myPointer = &myVar;

tu dices:

myPointer = La dirección donde se almacenan los datos en myVar

  1. En el segundo ejemplo, el error no se activa, pero al hacer un cout del valor de myPointer todavía se devuelve una dirección de memoria

Devuelve una dirección que apunta a una ubicación de la memoria que se ha eliminado. Porque primero creas el puntero y asignas su valor a myPointer, segundo lo eliminas, tercero lo imprimes. Entonces, a menos que asigne otro valor a myPointer, la dirección eliminada permanecerá.

  1. ¿El # 3 realmente funciona? Me parece que funciona, el puntero ya no almacena una dirección, ¿es esta la forma correcta de eliminar un puntero?

NULL es igual a 0, elimina 0, por lo que no elimina nada. Y es lógico que imprima 0 porque lo hiciste:

myPointer = NULL;

que es igual a:

myPointer = 0;
fonZ
fuente
4
  1. Está intentando eliminar una variable asignada en la pila. No puedes hacer esto
  2. Eliminar un puntero no lo destruye en realidad, solo la memoria ocupada se devuelve al sistema operativo. Puede acceder a él hasta que la memoria se use para otra variable o se manipule de otra manera. Por lo tanto, es una buena práctica establecer un puntero en NULL (0) después de eliminar.
  3. Eliminar un puntero NULL no elimina nada.
Hakan Serce
fuente
2
int value, *ptr;

value = 8;
ptr = &value;
// ptr points to value, which lives on a stack frame.
// you are not responsible for managing its lifetime.

ptr = new int;
delete ptr;
// yes this is the normal way to manage the lifetime of
// dynamically allocated memory, you new'ed it, you delete it.

ptr = nullptr;
delete ptr;
// this is illogical, essentially you are saying delete nothing.
Casper Beyer
fuente
1
Además, consulte esta conferencia sobre marcos de pila youtube.com/watch?v=bjObm0hxIYY y youtube.com/watch?v=Rxvv9krECNw sobre punteros.
Casper Beyer