2009-10-03 13 views
7

Toda la documentación que puedo encontrar en los contenedores STL (cola y lista) dice que para cualquiera de las funciones de eliminación se llama al destructor del objeto eliminado. Esto significa que no puedo usar std :: queue en cualquier momento que quiera una cola que sea simplemente una lista de objetos que necesitan alguna operación realizada en ellos.¿Se ha eliminado de STL std :: queue sin destruir el objeto eliminado?

Quiero ser capaz de agregar objetos a la cola cuando están esperando en la cola para que les haga algo. Luego quiero eliminarlos cuando termine con ellos, sin destruir el objeto en cuestión. Esto no parece ser posible a partir de la documentación que he leído. ¿Estoy malinterpretando la documentación? ¿Hay otro tipo de cola en el STL que no sea la "cola" básica que no llama al destructor del objeto eliminado en una llamada a pop_front?

Editar para aclarar: En mi caso, estoy usando una lista de punteros. Algo como esto:

dbObject *someObject; 
    queue<dbObject *> inputQueue; 
    inputQueue.push_back(someObject); 

    ... 

    dbObject *objectWithInput = inputQueue.front(); 
    //handle object's input... 
    inputQueue.pop_front(); // Remove from queue... destroyed now? 
+1

si está almacenando punteros dentro de la cola, eliminarlos no llamará al destructor. –

+0

Una pregunta similar: http://stackoverflow.com/questions/1525535/delete-all-items-from-a-c-stdvector –

Respuesta

17

Si coloca punteros a objetos en la cola (y cualquier otro contenedor STL), los punteros no se eliminarán cuando los elimine.

Elaborar: cuando utiliza std :: queue y elimina un objeto, se llama al destructor de some_obj *. Pero el destructor para el puntero simple (o cualquier tipo de POD - int, char, etc.) está vacío, no funciona. La línea fina aquí es que el destructor para some_obj * es muy diferente del destructor para some_obj.

+2

Exactamente. Y solo como referencia, si realmente desea un contenedor para eliminar los objetos apuntados, entonces necesitaría almacenar algún tipo de puntero inteligente o bien usar algo como los contenedores de punteros de boost. – TheUndeadFish

3

¿Qué le parece usar una lista de punteros a los objetos?

+0

¿No seguiría llamando al deconstructor? Eso es lo que estoy usando, la mayoría de mis objetos son punteros a un objeto almacenado en otro lugar en el código. –

4
class someobj_t {}; 

std::queue<someobj_t> q; 
... 

someobj_t ppd = q.front(); // ppd is not a reference 
q.pop(); 

// ppd now contain removed object 

Si no desea someobj_t a copiar se puede utilizar std::queue< shared_ptr<someobj_t> >.

+0

Estoy usando una lista de punteros. –

+1

Entonces el objeto real no se destruirá en el borrado. –

7

Los contenedores STL tienen una semántica de valores. Cuando inserta un objeto en un contenedor STL, el contenedor STL guarda su propia copia del objeto, y cuando el objeto (copia interna) se elimina del contenedor, se destruye.

Si utilizó un contenedor de tipo proxy, como punteros sin formato, punteros inteligentes (shared_ptr, weak_ptr) o adaptadores (como boost :: reference_wrapper), el contenedor STL destruirá el proxy pero no el tipo. Elegir uno sobre los demás suele ser una cuestión de cómo desea tratar con los recursos.

La expresión más común es usar punteros crudos, pero no especifican quién está a cargo de la destrucción (¿el código que extrae del contenedor debe eliminar el puntero o el recurso se maneja en otro lugar?).

El uso moderno se mueve hacia el enfoque shared_ptr, ya que diluye el problema de propiedad. Se garantizará que el objeto esté activo cuando lo saque del contenedor, y si nadie más tiene un shared_ptr, entonces el objeto se eliminará automáticamente cuando el local shared_ptr quede fuera del alcance. El uso de un weak_ptr mantendrá la propiedad en el código original, pero le permitirá verificar la validez del puntero (si se eliminó) antes del uso. Esto podría permitirle evitar realizar la operación en un objeto que se eliminará de inmediato.

El problema con el enfoque shared_ptr/weak_ptr es que te obliga a utilizar shared_ptr para contener el recurso original. Esto significa que no podrá poner un puntero en un subobjeto (atributo miembro) de otra clase sin rediseñar la clase para mantener el atributo a través de un shared_ptr, y eso tendrá otras implicaciones (los atributos ya no serán contiguos en la memoria) , se requerirán operaciones de asignación más dinámicas ...)

Una técnica que apenas se ve es el uso de adaptadores como boost :: reference_wrapper <>. Un contenedor de referencia es un objeto proxy que contiene una referencia al objeto original y se puede copiar por sí mismo. La ventaja sobre los punteros brutos simples es que al leer el código está claro que el recurso se gestiona fuera de la cola: el código que extrae los datos de la cola no necesita eliminar el objeto. La ventaja sobre el enfoque del puntero inteligente es que no necesita rediseñar otras partes de su sistema para usar punteros inteligentes. La desventaja es que, como en el enfoque del puntero sin formato, debe asegurarse de que la duración del objeto referido supere la referencia en el contenedor de forma manual.

+0

Me gusta la forma en que explicó y redactó esta respuesta, muy útil y muy completa. – joshperry

2

Piense que el artículo está en un contenedor "dentro del alcance" de ese contenedor, cuando se saca un artículo de un contenedor es como dejar el alcance de una función. Si la variable es un puntero, al elemento no le sucede nada al salir del alcance. Si la variable es una pila local, se llamará automáticamente al destructor al salir del alcance.

El almacenamiento de punteros en contenedores tiene los mismos inconvenientes que la asignación en un puntero local sin formato, la memoria no se limpia automáticamente. En una función, si no elimina el puntero ni transfiere la propiedad devolviéndola, entonces tiene una pérdida de memoria.

Al almacenar punteros sin procesar en un contenedor, la propiedad puede ser un poco ambigua y las filtraciones pueden suceder fácilmente. Eche un vistazo a tr1 :: shared_ptr y almacénelo en los contenedores.

std :: unique_ptr en C++ 0x también sería una buena solución para almacenar un puntero en un contenedor stdlib cuando esté disponible.

Cuestiones relacionadas