2010-07-27 25 views
13

Busqué StackOverflow pero no pude encontrar la respuesta a esta pregunta.¿Cuál es la forma correcta de liberar un std :: vector de punteros en C++?

Supongamos que tengo un std::vector<Day *> vector_day - es decir - un vector de punteros a Day objeto. Ahora push_back a vector_day muchos elementos:

vector_day.push_back(new Day(12)); 
vector_day.push_back(new Day(99)); 
vector_day.push_back(new Day(71)); 
... 

ahora en algún momento ya no necesitan vector_day. ¿Cuál es la forma correcta de liberar la memoria?

Es esta la forma correcta:

for (std::vector<Day *>::iterator i = vector_day.begin(); i != vector_day.end(); ++i) { 
    delete *i; 
} 

¿Esto no invalida el vector en cada eliminación? Estoy muy confundido.

+0

puede ser una víctima de http://stackoverflow.com/questions/3054567/right-way-to-deallocate-an-stdvector-object –

+0

RC - no realmente, esto es completamente diferente. – bodacydo

+1

No coloque punteros así. ¿Qué sucede si se lanza una excepción entre el momento en que el material está en el vector y el lugar donde lo elimina todo? Te saltearías y perderías. Utilice un puntero inteligente o un contenedor de puntero, nunca punteros sin formato. – GManNickG

Respuesta

16

La mejor manera no es poner punteros en el vector en primer lugar si no es absolutamente necesario.

Pero si realmente necesita un vector de punteros, entonces la forma en que lo está haciendo está bien (pero .clear() los términos posteriores, si no se destruye inmediatamente, para que no esté lleno de colgantes) punteros)

la declaración

delete *it; 

tiene ningún efecto sobre el iterador. No cambia el iterador, invalida el iterador ni elimina el puntero al que hace referencia el iterador de la colección. Todo lo que hace es liberar la memoria a la que hace referencia el puntero al que apunta el iterador. El puntero en sí debe ser eliminado de la colección por separado.

+0

Gracias por explicarme. Entonces, cuando el vector sale del alcance, el puntero se eliminará automáticamente, ¿verdad? – bodacydo

+1

@bodacydo: Correcto. Si realiza las llamadas 'delete * it' justo antes de que el' vector' salga del ámbito, no se preocupe por llamar a 'clear()'. –

0

Las operaciones que agregan o quitan elementos de la matriz pueden invalidar los iteradores, consulte la documentación de reglas específicas para los diferentes tipos de contenedores. Con delete, actúa sobre los datos contenidos en los elementos de la matriz, no sobre la forma de la matriz. Los iteradores atraviesan la forma del contenedor, no les importa su contenido.

+0

Ahora entiendo mejor la invalidación. ¡Gracias por tu respuesta! – bodacydo

0

En primer lugar, cambiaste de i a it, pero supongo que es solo un error.

Pero para responder a su búsqueda, no, está bien. No está cambiando it, está cambiando *it.

+0

Corregí el error de 'it' que se usa para' i'. Gracias por detectarlo. ¿Qué pasa si hice 'i = 0xAABBCCDD' en el bucle - que claramente cambia' i' y no 'it', ¿eso invalidaría el vector? – bodacydo

1

Probablemente debería estar utilizando algún tipo de puntero administrado, muy probablemente un puntero compartido.

Si borra el vector mientras otra persona todavía se aferra a uno de esos indicadores, obtendrá un comportamiento muy desagradable si intentan desreferenciarlo. Un puntero compartido te salvará ese dolor de cabeza.

Si puede garantizar que nada más hará referencia a los punteros después de que se elimine el vector, entonces aún puede beneficiarse del uso de un puntero automático. Gestionará la desasignación cuando el vector se destruya. La sobrecarga es mínima, y ​​hace su vida mucho más fácil.

+0

Iría por unique_ptr, ciertamente no auto_ptr. – Gabriel

0

Está bien. Está borrando *i (objeto apuntado por el elemento del vector) y no i (elemento del vector), por lo que el vector no se invalida.

Consulte this question para ver un caso en el que el desarrollador también quiso eliminar todos i s, y la solución (vector_day.clear()) después del bucle.

3

Otra C++ manera de hacer esto es definir una estructura de ayuda:

struct delete_ptr { // Helper function to ease cleanup of container 
    template <typename P> 
    void operator() (P p) { 
     delete p; 
    } 
}; 

y luego usar los algoritmos:

std::for_each(vector_day.begin(), vector_day.end(), delete_ptr()); 
vector_day.clear(); 
2

En general en C++ debe ocultar la gestión de memoria tanto como sea posible para evitar errores de memoria A menos que esté copiando mucho los punteros y se preocupe mucho por el rendimiento, simplemente usaría un shared_ptr.

Es parte del estándar TR1 y está disponible en la mayoría de los compiladores C++ modernos listos para usar (http://anteru.net/2008/09/01/260/) y es ideal para disparar y olvidarse de la administración de la memoria.

+1

O, primero, debe mirar unique_ptr. – DanDan

5

Boost ptr_vector al rescate!

hace exactamente lo que necesita, sin necesidad de repetir y borrar el contenido de la std :: vector

0

Aquí está una clase práctica que escribí hace un tiempo al tratar con el mismo problema. Estaba convirtiendo algunos códigos de viejos vectores y listas basados ​​en RogueWave en vectores y listas basados ​​en STL, y necesitaba algún modo de emular el método clearAndDestroy() de RW para las listas de punteros. El método clearAndDestroy() puede anularse para manejar diferentes tipos de estructuras (solo incluí el vector aquí para abreviar).

class StlUtils 
{ 
    public: 

     /** 
     * This method provides a templated way to destroy a std::vector 
     * full of pointers. It is basically a replacement for the RW 
     * vector class' clearAndDestroy methods. The list argument is 
     * returned empty. 
     * 
     * @param list the list of pointers to be destroyed. 
     */ 
     template<class T> static void clearAndDestroy(
     std::vector<T*> &itemList) 
     { 
     for_each(itemList.begin(), itemList.end(), 
        stl_deleter<T>()); 
     itemList.clear(); 
     } 

    private: 

     /** 
     * Templated member function for use with the clearAndDestroy() 
     * method. It provides the method needed by for_each to do the 
     * actual deletion. 
     */ 
     template<class T> struct stl_deleter 
     { 
     void operator() (T* x) { 
      if (x != NULL) 
       delete x; 
     } 
     }; 
}; 
+0

Tiene permiso para eliminar punteros nulos (lo que no hará nada), por lo que el if no es realmente necesario. – ollb

Cuestiones relacionadas