¿Es una buena práctica anular un puntero después de eliminarlo?

150

Comenzaré diciendo, use punteros inteligentes y nunca tendrá que preocuparse por esto.

¿Cuáles son los problemas con el siguiente código?

Foo * p = new Foo;
// (use p)
delete p;
p = NULL;

Esto fue provocado por una respuesta y comentarios a otra pregunta. Un comentario de Neil Butterworth generó algunos votos a favor:

Establecer punteros en NULL después de eliminar no es una buena práctica universal en C ++. Hay momentos en que es bueno hacerlo, y momentos en los que no tiene sentido y puede ocultar errores.

Hay muchas circunstancias en las que no ayudaría. Pero en mi experiencia, no puede doler. Alguien me ilumine.

Mark Ransom
fuente
66
@Andre: Técnicamente, no está definido. Es probable que suceda que acceda a la misma memoria que antes, pero que ahora puede ser utilizada por otra cosa. Si elimina la memoria dos veces, es probable que arruine la ejecución de su programa de una manera difícil de encontrar. Sin deleteembargo, es seguro para un puntero nulo, que es una razón por la cual poner a cero un puntero puede ser bueno.
David Thornley
55
@ André Pena, no está definido. A menudo ni siquiera es repetible. Establece el puntero en NULL para que el error sea más visible al depurar, y tal vez para hacerlo más repetible.
Mark Ransom
3
@ André: Nadie lo sabe. Es un comportamiento indefinido. Puede bloquearse con una infracción de acceso, o puede sobrescribir la memoria utilizada por el resto de la aplicación. El estándar de idioma no garantiza lo que sucede, por lo que no puede confiar en su aplicación una vez que ha sucedido. Se podría haber disparado los misiles nucleares o formateado el disco duro. puede corromper la memoria de su aplicación o puede hacer que los demonios salgan volando de su nariz. Todas las apuestas están cerradas.
jalf
16
Los demonios voladores son una característica, no un error.
jball
3
Esta pregunta no es un duplicado porque la otra pregunta es sobre C y esta es sobre C ++. Muchas de las respuestas dependen de cosas como punteros inteligentes, que no están disponibles en C ++.
Adrian McCarthy

Respuestas:

86

Establecer un puntero a 0 (que es "nulo" en C ++ estándar, la definición NULL de C es algo diferente) evita bloqueos en borrados dobles.

Considera lo siguiente:

Foo* foo = 0; // Sets the pointer to 0 (C++ NULL)
delete foo; // Won't do anything

Mientras:

Foo* foo = new Foo();
delete foo; // Deletes the object
delete foo; // Undefined behavior 

En otras palabras, si no establece punteros eliminados a 0, se meterá en problemas si está haciendo doble eliminación. Un argumento en contra de establecer punteros en 0 después de la eliminación sería que al hacerlo solo enmascara los errores de doble eliminación y los deja sin controlar.

Obviamente, es mejor no tener errores de doble eliminación, pero dependiendo de la semántica de propiedad y los ciclos de vida de los objetos, esto puede ser difícil de lograr en la práctica. Prefiero un error de doble borrado enmascarado sobre UB.

Finalmente, una nota al margen sobre la administración de la asignación de objetos, le sugiero que eche un vistazo a la std::unique_ptrpropiedad estricta / singular, std::shared_ptra la propiedad compartida u otra implementación de puntero inteligente, según sus necesidades.

Håvard S
fuente
13
Su aplicación no siempre fallará en una doble eliminación. Dependiendo de lo que ocurra entre las dos eliminaciones, cualquier cosa podría suceder. Lo más probable es que corrompa su montón y se bloqueará en algún momento más adelante en un código completamente no relacionado. Si bien una segfault generalmente es mejor que ignorar silenciosamente el error, la segfault no está garantizada en este caso, y es de utilidad cuestionable.
Adam Rosenfield
28
El problema aquí es el hecho de que tiene una doble eliminación. Hacer que el puntero sea NULL solo oculta el hecho de que no lo corrige ni lo hace más seguro. Imagaine, una mainainer, regresa un año después y ve que borran a foo. Ahora cree que puede reutilizar el puntero, desafortunadamente puede perder la segunda eliminación (puede que ni siquiera esté en la misma función) y ahora la reutilización del puntero ahora se vuelve basura por la segunda eliminación. Cualquier acceso después de la segunda eliminación ahora es un problema importante.
Martin York
11
Es cierto que la configuración del puntero NULLpuede enmascarar un error de eliminación doble. (Algunos podrían considerar que esta máscara es realmente una solución; lo es, pero no es muy buena, ya que no llega a la raíz del problema). Pero no establecerla en máscaras NULL en el extremo (¡LEJOS!) Más Problemas comunes de acceso a los datos después de que se han eliminado.
Adrian McCarthy
Que yo sepa, std :: auto_ptr ya no se utiliza en el próximo estándar de C ++
rafak
No diría que está en desuso, parece que la idea simplemente desapareció. Más bien, está siendo reemplazado por unique_ptr, que hace lo que auto_ptrintentó hacer, con semántica de movimiento.
GManNickG
55

Establecer punteros en NULL después de haber eliminado lo que apuntaba ciertamente no puede dañar, pero a menudo es una especie de curita sobre un problema más fundamental: ¿Por qué estás usando un puntero en primer lugar? Puedo ver dos razones típicas:

  • Simplemente quería algo asignado en el montón. En cuyo caso, envolverlo en un objeto RAII hubiera sido mucho más seguro y limpio. Finalice el alcance del objeto RAII cuando ya no lo necesite. Así es como std::vectorfunciona, y resuelve el problema de dejar accidentalmente punteros para desasignar la memoria. No hay punteros.
  • O tal vez quería una semántica compleja de propiedad compartida. Es posible que el puntero devuelto desde newno sea el mismo que el deleteinvocado. Varios objetos pueden haber usado el objeto simultáneamente mientras tanto. En ese caso, habría sido preferible un puntero compartido o algo similar.

Mi regla general es que si dejas punteros en el código de usuario, lo estás haciendo mal. El puntero no debería estar allí para señalar basura en primer lugar. ¿Por qué no hay un objeto que se responsabilice de garantizar su validez? ¿Por qué su alcance no termina cuando lo hace el objeto señalado?

jalf
fuente
15
Entonces, ¿estás argumentando que no debería haber habido un puntero en bruto en primer lugar, y que cualquier cosa que involucre dicho puntero no debería ser bendecido con el término "buena práctica"? Lo suficientemente justo.
Mark Ransom
77
Bueno, mas o menos. No diría que nada que implique un puntero en bruto puede considerarse una buena práctica. Solo que es la excepción más que la regla. Por lo general, la presencia del puntero es un indicador de que hay algo mal en un nivel más profundo.
jalf
3
pero para responder la pregunta inmediata, no, no veo cómo establecer punteros en nulo puede causar errores.
jalf
77
No estoy de acuerdo: hay casos en que un puntero es bueno para usar. Por ejemplo, hay 2 variables en la pila y desea elegir una de ellas. O desea pasar una variable opcional a una función. Yo diría que nunca debes usar un puntero en bruto junto con new.
rlbond
44
cuando un puntero se ha salido del alcance, no veo cómo algo o alguien podría necesitar lidiar con él.
jalf
43

Tengo una mejor práctica aún mejor: ¡Donde sea posible, finalice el alcance de la variable!

{
    Foo* pFoo = new Foo;
    // use pFoo
    delete pFoo;
}
Don Neufeld
fuente
17
Sí, RAII es tu amigo. Envuélvelo en una clase y se vuelve aún más simple. ¡O no maneje la memoria usted mismo usando STL!
Brian
24
Sí, de hecho, esa es la mejor opción. Sin embargo, no responde la pregunta.
Mark Ransom
3
Esto parece ser solo un subproducto del uso del período de alcances de funciones, y realmente no aborda el problema. Cuando usa punteros, generalmente pasa copias de ellos de varias capas de profundidad y luego su método realmente no tiene sentido en un esfuerzo por abordar el problema. Si bien estoy de acuerdo en que un buen diseño lo ayudará a aislar los errores, no creo que su método sea el medio principal para ese fin.
San Jacinto
2
Ahora que lo pienso, si pudieras hacer esto, ¿por qué no olvidarías el montón y sacarías toda tu memoria de la pila?
San Jacinto
44
Mi ejemplo es intencionalmente mínimo. Por ejemplo, en lugar de nuevo, tal vez el objeto sea creado por una fábrica, en cuyo caso no puede ir a la pila. O tal vez no se crea al comienzo del alcance, sino que se encuentra en alguna estructura. Lo que estoy ilustrando es que este enfoque encontrará cualquier mal uso del puntero en el momento de la compilación , mientras que NULLing lo encontrará en el tiempo de ejecución .
Don Neufeld
31

Siempre establezco un puntero a NULL(ahora nullptr) después de eliminar los objetos a los que apunta.

  1. Puede ayudar a atrapar muchas referencias a la memoria liberada (suponiendo que la plataforma falla en un puntero nulo).

  2. No captará todas las referencias a la memoria libre si, por ejemplo, tiene copias del puntero por ahí. Pero algo es mejor que nada.

  3. Enmascarará una eliminación doble, pero creo que son mucho menos comunes que los accesos a la memoria ya liberada.

  4. En muchos casos, el compilador lo va a optimizar. Entonces el argumento de que es innecesario no me convence.

  5. Si ya está utilizando RAII, entonces no hay muchos deletes en su código para comenzar, por lo que el argumento de que la asignación adicional causa desorden no me convence.

  6. A menudo es conveniente, al depurar, ver el valor nulo en lugar de un puntero obsoleto.

  7. Si esto todavía te molesta, utiliza un puntero inteligente o una referencia en su lugar.

También establecí otros tipos de identificadores de recursos en el valor sin recurso cuando el recurso se libera (que normalmente solo está en el destructor de un contenedor RAII escrito para encapsular el recurso).

Trabajé en un producto comercial grande (9 millones de declaraciones) (principalmente en C). En un momento, usamos macro magia para anular el puntero cuando se liberó la memoria. Esto inmediatamente expuso muchos errores al acecho que se solucionaron rápidamente. Hasta donde puedo recordar, nunca tuvimos un error doblemente libre.

Actualización: Microsoft cree que es una buena práctica para la seguridad y recomienda la práctica en sus políticas SDL. Aparentemente, MSVC ++ 11 pisoteará el puntero eliminado automáticamente (en muchas circunstancias) si compila con la opción / SDL.

Adrian McCarthy
fuente
12

En primer lugar, hay muchas preguntas existentes sobre este tema y temas relacionados, por ejemplo, ¿Por qué no elimina establecer el puntero en NULL? .

En su código, el problema de lo que sucede en (use p). Por ejemplo, si en algún lugar tiene un código como este:

Foo * p2 = p;

luego establecer p en NULL logra muy poco, ya que todavía tiene que preocuparse por el puntero p2.

Esto no quiere decir que establecer un puntero en NULL siempre sea inútil. Por ejemplo, si p fuera una variable miembro que apunta a un recurso cuya vida útil no era exactamente la misma que la clase que contiene p, entonces establecer p en NULL podría ser una forma útil de indicar la presencia o ausencia del recurso.

Comunidad
fuente
1
Estoy de acuerdo en que hay momentos en que no ayudará, pero parece implicar que podría ser activamente dañino. ¿Fue esa tu intención o lo leí mal?
Mark Ransom
1
Si hay una copia del puntero es irrelevante para la pregunta de si la variable del puntero debe establecerse en NULL. Establecerlo en NULL es una buena práctica por los mismos motivos, limpiar los platos después de que haya terminado con la cena es una buena práctica, aunque no es una protección contra todos los errores que puede tener un código, sí promueve la buena salud del código.
Franci Penov
2
@Franci Mucha gente parece estar en desacuerdo contigo. Y si hay una copia ciertamente es relevante si intenta usar la copia después de eliminar el original.
3
Franci, hay una diferencia. Lavas los platos porque los vuelves a usar. No necesita el puntero después de eliminarlo. Debería ser lo último que hagas. Una mejor práctica es evitar la situación por completo.
GManNickG
1
Puede reutilizar una variable, pero ya no es un caso de programación defensiva; es cómo diseñó la solución al problema en cuestión. El OP está discutiendo si este estilo defensivo es algo por lo que deberíamos esforzarnos, no si alguna vez estableceremos un puntero como nulo. E idealmente, a su pregunta, ¡sí! ¡No use punteros después de eliminarlos!
GManNickG
7

Si hay más código después de delete, Sí. Cuando el puntero se elimina en un constructor o al final del método o función, No.

El objetivo de esta parábola es recordarle al programador, durante el tiempo de ejecución, que el objeto ya ha sido eliminado.

Una práctica aún mejor es usar punteros inteligentes (compartidos o con alcance) que eliminan automáticamente sus objetos de destino.

Thomas Matthews
fuente
Todos (incluido el interrogador original) están de acuerdo en que los punteros inteligentes son el camino a seguir. El código evoluciona. Es posible que no haya más código después de la eliminación cuando lo corrige por primera vez, pero es probable que eso cambie con el tiempo. Poner la tarea ayuda cuando eso sucede (y no cuesta casi nada mientras tanto).
Adrian McCarthy
3

Como han dicho otros, delete ptr; ptr = 0;no va a hacer que los demonios salgan volando de tu nariz. Sin embargo, fomenta el uso de ptruna especie de bandera. El código se llena de basura deletey establece el puntero en NULL. El siguiente paso es dispersarse a if (arg == NULL) return;través de su código para protegerse contra el uso accidental de un NULLpuntero. El problema se produce una vez que las comprobaciones se NULLconvierten en su medio principal para verificar el estado de un objeto o programa.

Estoy seguro de que hay un olor a código sobre el uso de un puntero como bandera en algún lugar, pero no he encontrado uno.

D.Shawley
fuente
9
No hay nada de malo en usar un puntero como bandera. Si está utilizando un puntero y NULLno es un valor válido, entonces probablemente debería estar utilizando una referencia en su lugar.
Adrian McCarthy
2

Cambiaré tu pregunta ligeramente:

¿Usarías un puntero no inicializado? ¿Sabes, uno que no estableciste en NULL o no asignaste la memoria a la que apunta?

Hay dos escenarios en los que se puede omitir establecer el puntero en NULL:

  • la variable de puntero queda fuera de alcance inmediatamente
  • ha sobrecargado la semántica del puntero y está utilizando su valor no solo como un puntero de memoria, sino también como una clave o valor sin formato. Sin embargo, este enfoque tiene otros problemas.

Mientras tanto, argumentar que establecer el puntero en NULL podría ocultarme errores suena como argumentar que no debe corregir un error porque la solución podría ocultar otro error. Los únicos errores que podrían aparecer si el puntero no está configurado en NULL serían los que intentan usar el puntero. Pero establecerlo en NULL en realidad causaría exactamente el mismo error que se mostrará si lo usa con memoria liberada, ¿no?

Franci Penov
fuente
(A) "suena como argumentar que no debe corregir un error" No establecer un puntero en NULL no es un error. (B) "Pero establecerlo en NULL realmente causaría exactamente el mismo error" No. La configuración en NULL oculta la eliminación doble . (C) Resumen: la configuración en NULL oculta la eliminación doble, pero expone las referencias obsoletas. No establecer en NULL puede ocultar referencias obsoletas, pero expone dobles eliminaciones. Ambas partes están de acuerdo en que el verdadero problema es corregir las referencias obsoletas y las eliminaciones dobles.
Mooing Duck
2

Si no tiene otra restricción que lo obligue a establecer o no establecer el puntero en NULL después de eliminarlo (una de esas restricciones fue mencionada por Neil Butterworth ), entonces mi preferencia personal es dejarlo así.

Para mí, la pregunta no es "¿es una buena idea?" pero "¿qué comportamiento evitaría o permitiría tener éxito al hacer esto?" Por ejemplo, si esto permite que otro código vea que el puntero ya no está disponible, ¿por qué otro código incluso intenta mirar los punteros liberados después de liberarlos? Por lo general, es un error.

También realiza más trabajo del necesario, además de obstaculizar la depuración post mortem. Cuanto menos toque la memoria después de que no la necesite, más fácil será descubrir por qué algo falló. Muchas veces me he basado en el hecho de que la memoria está en un estado similar al de un error en particular para diagnosticar y corregir dicho error.

MSN
fuente
2

La anulación explícita después de eliminar sugiere fuertemente a un lector que el puntero representa algo que es conceptualmente opcional . Si veo que se está haciendo eso, comenzaría a preocuparme de que en todas partes en la fuente que se utiliza el puntero se pruebe primero con NULL.

Si eso es lo que realmente quiere decir, es mejor hacerlo explícito en la fuente usando algo como boost :: opcional

optional<Foo*> p (new Foo);
// (use p.get(), but must test p for truth first!...)
delete p.get();
p = optional<Foo*>();

Pero si realmente quisieras que la gente supiera que el puntero ha "ido mal", estaré totalmente de acuerdo con aquellos que dicen que lo mejor que puedes hacer es dejarlo fuera de alcance. Luego está utilizando el compilador para evitar la posibilidad de malas desreferencias en tiempo de ejecución.

Ese es el bebé en toda el agua de baño C ++, no debería tirarlo. :)

HostileFork dice que no confíes en SE
fuente
2

En un programa bien estructurado con verificación de errores adecuada, no hay razón para no asignarlo como nulo. 0se destaca solo como un valor inválido universalmente reconocido en este contexto. Falla duro y falla pronto.

Muchos de los argumentos en contra de la asignación 0sugieren que podría ocultar un error o complicar el flujo de control. Básicamente, se trata de un error ascendente (no es su culpa (perdón por el mal juego de palabras)) u otro error en nombre del programador, tal vez incluso una indicación de que el flujo del programa se ha vuelto demasiado complejo.

Si el programador quiere introducir el uso de un puntero que puede ser nulo como un valor especial y escribir todo lo necesario para evitarlo, esa es una complicación que han introducido deliberadamente. Cuanto mejor sea la cuarentena, antes encontrará casos de uso indebido y menos podrán propagarse a otros programas.

Los programas bien estructurados pueden diseñarse utilizando características de C ++ para evitar estos casos. Puede usar referencias, o simplemente puede decir "pasar / usar argumentos nulos o inválidos es un error", un enfoque que es igualmente aplicable a los contenedores, como los punteros inteligentes. El aumento del comportamiento constante y correcto prohíbe que estos errores lleguen lejos.

A partir de ahí, solo tiene un alcance y contexto muy limitados donde puede existir un puntero nulo (o está permitido).

Lo mismo puede aplicarse a los punteros que no lo son const. Seguir el valor de un puntero es trivial porque su alcance es muy pequeño y el uso incorrecto está verificado y bien definido. Si su conjunto de herramientas e ingenieros no pueden seguir el programa después de una lectura rápida o si hay una verificación de error inapropiada o un flujo de programa inconsistente / indulgente, tiene otros problemas mayores.

Finalmente, su compilador y su entorno probablemente tengan algunas protecciones para los momentos en que le gustaría introducir errores (garabatear), detectar accesos a la memoria liberada y capturar otros UB relacionados. También puede introducir diagnósticos similares en sus programas, a menudo sin afectar los programas existentes.

justin
fuente
1

Déjame expandir lo que ya has puesto en tu pregunta.

Esto es lo que ha puesto en su pregunta, en forma de viñeta:


Establecer punteros en NULL después de eliminar no es una buena práctica universal en C ++. Hay momentos en que:

  • es algo bueno hacer
  • y momentos en los que no tiene sentido y puede ocultar errores.

Sin embargo, ¡no hay momentos en que esto sea malo ! Usted no introducir más errores por anulando de forma explícita, no tendrá fugas de memoria, usted no causa un comportamiento indefinido a suceder.

Entonces, en caso de duda, simplemente anúlelo.

Dicho esto, si siente que tiene que anular explícitamente algún puntero, me parece que no ha dividido un método lo suficiente, y debería considerar el enfoque de refactorización llamado "Método de extracción" para dividir el método en partes separadas.

Lasse V. Karlsen
fuente
No estoy de acuerdo con "no hay momentos en que esto sea malo". Considere la cantidad de cortador que presenta este modismo. Se incluye un encabezado en cada unidad que elimina algo, y todas esas ubicaciones de eliminación se vuelven un poco menos sencillas.
GManNickG
Hay momentos en que es malo. Si alguien intenta desreferenciar su puntero eliminado ahora nulo cuando no debería, probablemente no se bloqueará y ese error está 'oculto'. Si hacen referencia a su puntero eliminado que todavía tiene algún valor aleatorio, es probable que lo note y el error será más fácil de ver.
Carson Myers
@Carson: Mi experiencia es todo lo contrario: anular la referencia a un nullptr casi siempre bloquea la aplicación y puede ser detectado por un depurador. Anular la referencia a un puntero colgante generalmente no produce un problema de inmediato, pero a menudo solo dará lugar a resultados incorrectos u otros errores en el futuro.
MikeMB
@MikeMB Estoy completamente de acuerdo, mis puntos de vista sobre esto han cambiado sustancialmente en los últimos ~ 6.5 años
Carson Myers
En términos de ser programadores, todos éramos alguien más hace 6-7 años :) Ni siquiera estoy seguro de haberme atrevido a responder una pregunta C / C ++ hoy :)
Lasse V. Karlsen
1

Si.

El único "daño" que puede hacer es introducir ineficiencia (una operación de almacenamiento innecesaria) en su programa, pero esta sobrecarga será insignificante en relación con el costo de asignar y liberar el bloque de memoria en la mayoría de los casos.

Si no lo hace, usted va a tener algunos errores derefernce puntero desagradables un día.

Siempre uso una macro para eliminar:

#define SAFEDELETE(ptr) { delete(ptr); ptr = NULL; }

(y similar para una matriz, free (), tiradores de liberación)

También puede escribir métodos de "eliminación automática" que tomen una referencia al puntero del código de llamada, de modo que fuercen el puntero del código de llamada a NULL. Por ejemplo, para eliminar un subárbol de muchos objetos:

static void TreeItem::DeleteSubtree(TreeItem *&rootObject)
{
    if (rootObject == NULL)
        return;

    rootObject->UnlinkFromParent();

    for (int i = 0; i < numChildren)
       DeleteSubtree(rootObject->child[i]);

    delete rootObject;
    rootObject = NULL;
}

editar

Sí, estas técnicas violan algunas reglas sobre el uso de macros (y sí, en estos días probablemente podría lograr el mismo resultado con las plantillas), pero al usarlas durante muchos años nunca he accedido a la memoria muerta, una de las más desagradables y difíciles La mayor parte del tiempo para depurar los problemas que puede enfrentar. En la práctica, durante muchos años, han eliminado eficazmente una gran variedad de errores de cada equipo en el que los he presentado.

También hay muchas maneras de implementar lo anterior: solo estoy tratando de ilustrar la idea de obligar a las personas a anular un puntero si eliminan un objeto, en lugar de proporcionarles un medio para liberar la memoria que no anula el puntero de la persona que llama. .

Por supuesto, el ejemplo anterior es solo un paso hacia un puntero automático. Lo cual no sugerí porque el OP estaba preguntando específicamente sobre el caso de no usar un puntero automático.

Jason Williams
fuente
2
Las macros son una mala idea, especialmente cuando parecen funciones normales. Si desea hacer esto, use una función con plantilla.
2
Wow ... nunca he visto algo así como anObject->Delete(anObject)invalidar el anObjectpuntero. Eso es aterrador. Debe crear un método estático para esto, de modo que se vea obligado a hacerlo TreeItem::Delete(anObject)al menos.
D.Shawley
Lo sentimos, lo escribí como una función en lugar de usar el formulario apropiado en mayúsculas "esto es una macro". Corregido También agregué un comentario para explicarme mejor.
Jason Williams
Y tienes razón, ¡mi ejemplo rápidamente descartado fue basura! Corregido :-). Simplemente estaba tratando de pensar en un ejemplo rápido para ilustrar esta idea: cualquier código que elimine un puntero debería garantizar que el puntero esté establecido en NULL, incluso si otra persona (la persona que llama) posee ese puntero. Por lo tanto, siempre pase una referencia al puntero para que se pueda forzar a NULL en el punto de eliminación.
Jason Williams
1

"Hay momentos en los que es bueno hacerlo, y momentos en los que no tiene sentido y puede ocultar errores"

Puedo ver dos problemas: Ese código simple:

delete myObj;
myobj = 0

se convierte en un for-liner en un entorno multiproceso:

lock(myObjMutex); 
delete myObj;
myobj = 0
unlock(myObjMutex);

La "mejor práctica" de Don Neufeld no se aplica siempre. Por ejemplo, en un proyecto automotriz tuvimos que establecer punteros a 0 incluso en destructores. Me imagino que en el software crítico para la seguridad tales reglas no son infrecuentes. Es más fácil (y sabio) seguirlos que tratar de persuadir al equipo / verificador de código para cada uso de puntero en el código, de que una línea que anula este puntero es redundante.

Otro peligro es confiar en esta técnica en el código de uso de excepciones:

try{  
   delete myObj; //exception in destructor
   myObj=0
}
catch
{
   //myObj=0; <- possibly resource-leak
}

if (myObj)
  // use myObj <--undefined behaviour

En dicho código, usted produce pérdida de recursos y pospone el problema o el proceso se bloquea.

Entonces, estos dos problemas que pasan por mi cabeza espontáneamente (Herb Sutter seguramente diría más) me hacen todas las preguntas del tipo "Cómo evitar el uso de punteros inteligentes y hacer el trabajo de manera segura con punteros normales" como obsoletos.

Valentin Heinitz
fuente
No puedo ver cómo el 4-liner es significativamente más complejo que un 3-liner (uno debería usar lock_guards de todos modos) y si su destructor arroja usted está en problemas de todos modos.
MikeMB
Cuando vi esta respuesta por primera vez, no entendí por qué querrías anular un puntero en un destructor, pero ahora sí: ¡es para el caso en el que el objeto que posee el puntero se usa después de que se elimina!
Mark Ransom
0

Siempre hay que preocuparse por los Punteros colgantes .

GrayWizardx
fuente
1
¿Sería tan difícil poner "punteros colgantes" en lugar de "esto"? :) Al menos le da a tu respuesta algo de sustancia.
GManNickG
44
Incluso es el tema de una sugerencia de control de calidad del W3C, "No use 'haga clic aquí' como texto de enlace": w3.org/QA/Tips/noClickHere
fuera el
0

Si va a reasignar el puntero antes de usarlo nuevamente (desreferenciarlo, pasarlo a una función, etc.), hacer que el puntero sea NULL es solo una operación adicional. Sin embargo, si no está seguro de si se reasignará o no antes de volver a usarlo, es una buena idea establecerlo en NULL.

Como muchos han dicho, es mucho más fácil usar punteros inteligentes.

Editar: como dijo Thomas Matthews en esta respuesta anterior , si un puntero se elimina en un destructor, no hay necesidad de asignarle NULL ya que no se volverá a usar porque el objeto ya se está destruyendo.

Dustin
fuente
0

Me imagino que configurar un puntero en NULL después de eliminarlo es útil en casos excepcionales en los que existe un escenario legítimo de reutilizarlo en una sola función (u objeto). De lo contrario, no tiene sentido, un puntero necesita señalar algo significativo mientras exista, punto.

Nemanja Trifunovic
fuente
0

Si el código no pertenece a la parte más crítica del rendimiento de su aplicación, manténgalo simple y use shared_ptr:

shared_ptr<Foo> p(new Foo);
//No more need to call delete

Realiza el recuento de referencias y es seguro para subprocesos. Puede encontrarlo en el tr1 (espacio de nombres std :: tr1, #include <memoria>) o si su compilador no lo proporciona, obténgalo de boost.

beta4
fuente