2010-10-14 22 views
5

Tengo un código que usa muchos punteros que apuntan a la misma dirección. dado un ejemplo sencillo equivalentes:Cómo eliminar de forma segura varios punteros

int *p = new int(1); 
int *q = p; 
int *r = q; 

delete r; r = NULL; // ok 
// delete q; q = NULL; // NOT ok 
// delete p; p = NULL; // NOT ok 

cómo eliminar de forma segura y sin eliminar múltiples? Esto es especialmente difícil si tengo muchos objetos que apuntan todos con punteros a la misma dirección.

+1

¿No debería funcionar? delete null se especifica en el estándar, por lo que permitió y debería funcionar. De acuerdo, no es el mejor estilo de codificación ... –

+0

@Mario: Eliminar NULL se especifica como NO-OP, pero llamarlo genera cierta sobrecarga. –

+6

El problema es q yp no será NULO, por lo que habrá doble eliminación. –

Respuesta

18

La respuesta, sin recurrir a los punteros administrados, es que debe saber si eliminar o no un puntero en función de dónde fue asignado.

Su ejemplo es algo inventado, pero en una aplicación del mundo real, el objeto responsable de asignar memoria sería responsable de destruirlo. Los métodos y funciones que reciben punteros ya inicializados y los almacenan durante un tiempo no eliminan esos punteros; esa responsabilidad recae en cualquier objeto originalmente asignado a la memoria.

Recuerde que sus llamadas al new deben equilibrarse con sus llamadas al delete. Cada vez que asigna memoria, usted sabe tiene que escribir el código de equilibrio (a menudo un destructor) para desasignar esa memoria.

+0

IMO esta es la mejor respuesta. –

+2

+1: Mi opinión es que shared_ptr solo debe usarse en el caso de esquina donde no hay un propietario claro de la memoria y que en la mayoría de los casos no lo necesita. – n1ckp

+0

Lea: http://crazyeddiecpp.blogspot.com/2010/12/pet-peeve.html - ¡no borre los punteros! –

28

Su herramienta es shared_ptr de la biblioteca boost. Echar un vistazo a la documentación: http://www.boost.org/doc/libs/1_44_0/libs/smart_ptr/shared_ptr.htm

Ejemplo:

void func() { 
    boost::shared_ptr<int> p(new int(10)); 
    boost::shared_ptr<int> q(p); 
    boost::shared_ptr<int> r(q); 

    // will be destructed correctly when they go out of scope. 
} 
+3

+1 - El escenario de OP de 'muchos punteros que apuntan a la misma dirección' es ideal para 'shared_ptr' –

+1

Los compiladores recientemente publicados proporcionan shared_ptr como miembro del espacio de nombres tr1. Por lo tanto, no es necesario aumentar, puede usar tr1 :: shared_ptr en su lugar. – nobar

+1

Los punteros "en bruto" en C++ se heredan de C, pero el problema es que no expresan propiedad en absoluto (es uno de los problemas que no se pueden mencionar con la suficiente frecuencia, en mi humilde opinión). Una de las cosas más importantes del código es que debe expresar las intenciones del autor (incluida la propiedad), por lo que siempre es mejor usar los indicadores inteligentes de impulso, o al menos ** std :: auto_ptr **. (Pero los indicadores de la vieja escuela están bien para casos simples, como internos para objetos de "comportamiento trivial"). – riviera

10

La respuesta "moderna" es el uso de un puntero inteligente y no hacer ningún eliminaciones manuales.

boost::shared_ptr<int> p(new int(1)); 
boost::shared_ptr<int> q = p; 
boost::shared_ptr<int> r = q; 

Fin de la historia!

+0

+1 para finalizar la historia ';)' –

+0

-1 para publicar el código no probado. Una buena API de puntero inteligente (como de std :: auto_ptr de boost :: shared_ptr) no debería permitir asignar punteros a punteros inteligentes o crear punteros inteligentes implícitos a partir de punteros utilizando el constructor de copias. De esta forma, la propiedad no sería obvia. Cambie su primera línea para impulsar :: shared_ptr p = boost :: shared_ptr (new int (1)); –

+0

gracias, código fijo –

0

Existen algunos casos muy raros en los que es posible que no pueda usar un puntero inteligente (probablemente se trata de un código anterior), pero tampoco puede usar un esquema simple de "propiedad".

Imagine que tiene un std::vector<whatever*> y algunos de los whatever* punteros apuntan al mismo objeto. La limpieza segura implica asegurarse de no eliminar el mismo elemento dos veces, de modo que construya un std::set<whatever*> sobre la marcha, y solo elimine los punteros que aún no están en el conjunto. Una vez que se han eliminado todos los objetos apuntados, ambos contenedores también se pueden eliminar de forma segura.

El valor de retorno de insert se puede utilizar para determinar si el elemento insertado era nuevo o no. No he probado el siguiente (o usado std :: set por un tiempo), pero creo que lo siguiente es correcto ...

if (myset.insert (pointervalue).second) 
{ 
    // Value was successfully inserted as a new item 
    delete pointervalue; 
} 

No debe diseñar proyectos de manera que esto es necesario, por supuesto, pero no es demasiado difícil lidiar con la situación si no puedes evitarlo.

3

El problema al que se enfrenta es que la semántica de propiedad en su programa no está clara. Desde el punto de vista del diseño, intente determinar quién es el propietario de los objetos en cada paso. En muchos casos, eso implicará que quien crea el objeto tendrá que eliminarlo más tarde, pero en otros casos la propiedad se puede transferir o incluso compartir.

Una vez que sepa a quién pertenece la memoria, vuelva al código y ejecútelo.Si un objeto es el único responsable de un objeto diferente, debe mantenerlo a través de un puntero inteligente de propiedad única (std::auto_ptr/unique_ptr) o incluso un puntero sin formato (intente evitar esto ya que es una fuente común de errores) y administre la memoria de forma manual. Luego pase referencias o punteros a otros objetos. Cuando se transfiere la propiedad, use las facilidades del puntero inteligente para ceder el objeto al nuevo propietario. Si la propiedad se comparte realmente (no hay un propietario claro del objeto asignado), puede usar shared_ptr y dejar que el puntero inteligente se ocupe de la administración de la memoria).

1

¿Por qué está intentando eliminar punteros arbitrariamente? Todos los objetos asignados dinámicamente se asignan en un lugar, por un propietario. Y debería ser responsabilidad de un propietario asegurarse de que el objeto se elimine de nuevo.

En algunos casos, es posible que desee transferir propiedad a otro objeto o componente, en cuyo caso la responsabilidad de eliminar también cambia.

Y a veces, solo quiere olvidarse de la propiedad y el uso compartido propietario: todos los que usan el objeto comparten el propietario, y mientras exista al menos un usuario, el objeto no se debe eliminar.

Luego usa shared_ptr.

En resumen, use RAII. No intente eliminar objetos manualmente.

0

No es posible saber si la memoria a la que hace referencia un puntero ya se ha eliminado, como en su caso.
Si no puede usar una biblioteca con punteros inteligentes, que hacen referencia al recuento, y no puede implementar su propio esquema de recuento de referencias (si realmente necesita hacer lo que describe en su publicación) intente llamar a realloc en los punteros.
He leído en otras publicaciones que, dependiendo de la implementación, la llamada a realloc puede no bloquearse pero devolver un puntero nulo. En este caso, usted sabe que la memoria a la que hace referencia ese puntero se ha liberado.
Si esto funciona como una solución sucia, no será portátil, pero si no tiene otra opción, pruébelo. Lo peor, por supuesto, será bloquear su aplicación :)

0

Yo diría que algunas veces, los indicadores inteligentes realmente pueden ralentizar su aplicación, aunque no mucho. Lo que yo haría es crear un método, así:

void safeDelete(void **ptr) 
{ 
    if(*ptr != NULL) 
    { 
    delete ptr; 
    *ptr = NULL; 
    } 
} 

No estoy seguro de si lo hice correctamente al 100%, pero lo que se hace es pasar el puntero en este método que toma el puntero de un Puntero, comprueba que el puntero al que apunta no esté establecido en NULO, luego elimina el objeto y luego establece la dirección en 0 o NULO. Corrígeme si esta no es una buena manera de hacerlo, también soy nuevo en esto y alguien me dijo que esta era una gran manera de verificar sin complicarse.

+0

Esto no resuelve el problema; con el ejemplo en la pregunta, 'safeDelete (& r)' funcionará, pero no modificará 'q' o' p', por lo que 'safeDelete (& q)' probablemente se bloquee. – Synxis

Cuestiones relacionadas