¿Std :: list :: remove método llama destructor de cada elemento eliminado?

std::list lst; //.... Node * node = /* get from somewhere pointer on my node */; lst.remove(node); 

¿Std :: list :: remove method call destructor (y memoria libre) de cada elemento eliminado? Si es así, ¿cómo puedo evitarlo?

Sí, quitar un Foo* de un contenedor destruye el Foo* , pero no liberará al Foo . Destruir un puntero sin procesar siempre es un no-op. No puede ser de otra manera! Déjame darte varias razones por qué.

Clase de almacenamiento

Eliminar un puntero solo tiene sentido si el puntero fue realmente asignado dinámicamente, pero ¿cómo podría el tiempo de ejecución saber si ese es el caso cuando se destruye la variable del puntero? Los punteros también pueden apuntar a variables estáticas y automáticas, y eliminar uno de esos comportamientos no definidos .

 { Foo x; Foo* p = &x; Foo* q = new Foo; // Has *q been allocated dynamically? // (The answer is YES, but the runtime doesn't know that.) // Has *p been allocated dynamically? // (The answer is NO, but the runtime doesn't know that.) } 

Punteros colgantes

No hay forma de averiguar si el pointee ya ha sido lanzado en el pasado. Eliminar el mismo puntero dos veces produce un comportamiento indefinido . (Se convierte en un puntero colgante después de la primera eliminación).

 { Foo* p = new Foo; Foo* q = p; // Has *q already been released? // (The answer is NO, but the runtime doesn't know that.) // (...suppose that pointees WOULD be automatically released...) // Has *p already been released? // (The answer WOULD now be YES, but the runtime doesn't know that.) } 

Punteros no inicializados

También es imposible detectar si una variable de puntero se ha inicializado en absoluto. ¿Adivina qué sucede cuando intentas eliminar ese puntero? Una vez más, la respuesta es un comportamiento indefinido .

  { Foo* p; // Has p been properly initialized? // (The answer is NO, but the runtime doesn't know that.) } 

Arrays dynamics

El sistema de tipo no distingue entre un puntero a un solo objeto ( Foo* ) y un puntero al primer elemento de una matriz de objetos (también Foo* ). Cuando se destruye una variable de puntero, el tiempo de ejecución no puede determinar si se libera la punta vía delete o mediante delete[] . La publicación a través de la forma incorrecta invoca un comportamiento indefinido .

 { Foo* p = new Foo; Foo* q = new Foo[100]; // What should I do, delete q or delete[] q? // (The answer is delete[] q, but the runtime doesn't know that.) // What should I do, delete p or delete[] p? // (The answer is delete p, but the runtime doesn't know that.) } 

Resumen

Dado que el tiempo de ejecución no puede hacer nada sensato con el punto, la destrucción de una variable de puntero es siempre un no-op. No hacer nada es definitivamente mejor que causar un comportamiento indefinido debido a una suposición desinformada 🙂

Consejo

En lugar de punteros crudos, considere usar punteros inteligentes como el tipo de valor de su contenedor, ya que asumen la responsabilidad de liberar el puntero cuando ya no es necesario. Dependiendo de su necesidad, use std::shared_ptr o std::unique_ptr . Si su comstackdor aún no es compatible con C ++ 0x, use boost::shared_ptr .

Nunca , repito, NUNCA VAYA A usar std::auto_ptr como el tipo de valor de un contenedor.

Llama al destructor de cada uno de los elementos de la list , pero no es un objeto Node . Es un Node* .

Por lo tanto, no elimina los punteros de Node .

¿Tiene sentido?

Llama al destructor de los datos en la lista. Eso significa que std::list::remove llamará al destructor de T (que es necesario cuando T es algo así como std::vector ).

En tu caso, llamaría al destructor del Node* , que no funciona. No llama al destructor del node .

Sí, aunque en este caso, Node * no tiene destructor. Sin embargo, dependiendo de sus aspectos internos, los diversos valores del nodo * se eliminan o destruyen según las reglas de scope. Si Node * donde algún tipo no fundamental, se llamaría un destructor.

¿Se llama al destructor en el nodo? No, pero ‘Nodo’ no es el tipo de elemento en la lista.

En cuanto a tu otra pregunta, no puedes. El contenedor de lista estándar (de hecho TODOS los contenedores estándar) adopta la propiedad de su contenido y lo limpiará. Si no desea que esto suceda, los contenedores estándar no son una buena opción.

Como está poniendo punteros en una std::list , no se std::list destructores en los objetos de Node apuntados.

Si desea almacenar objetos asignados en el montón en contenedores STL y hacer que se destruyan al eliminarlos, envuélvalos en un puntero inteligente como boost::shared_ptr

La mejor manera de entender es probar cada formulario y observar los resultados. Para utilizar hábilmente los objetos del contenedor con sus propios objetos personalizados, necesita tener una buena comprensión del comportamiento.

En resumen, para el tipo Node* no se llama al deconstructor ni se invoca delete / free; sin embargo, para el Node tipo se Node al deconstructor, mientras que la consideración de eliminar / libre es un detalle de implementación de la lista. Es decir, depende de si la implementación de la lista utilizó new / malloc.

En el caso de un unique_ptr , se invoca al deconstructor y la invocación de delete / free ocurrirá ya que se le debe dar algo asignado por new .

 #include  #include  #include  using namespace std; void* operator new(size_t size) { cout << "new operator with size " << size << endl; return malloc(size); } void operator delete(void *ptr) { cout << "delete operator for " << ptr << endl; free(ptr); } class Apple { public: int id; Apple() : id(0) { cout << "apple " << this << ":" << this->id << " constructed" << endl; } Apple(int id) : id(id) { cout << "apple " << this << ":" << this->id << " constructed" << endl; } ~Apple() { cout << "apple " << this << ":" << this->id << " deconstructed" << endl; } bool operator==(const Apple &right) { return this->id == right.id; } static void* operator new(size_t size) { cout << "new was called for Apple" << endl; return malloc(size); } static void operator delete(void *ptr) { cout << "delete was called for Apple" << endl; free(ptr); } /* The compiler generates one of these and simply assignments member variable. Think memcpy. It can be disabled by uncommenting the below requiring the usage of std::move or one can be implemented. */ //Apple& operator=(const Apple &from) = delete; }; int main() { list a = list(); /* deconstructor not called */ /* memory not released using delete */ cout << "test 1" << endl; a.push_back(new Apple()); a.pop_back(); /* deconstructor not called */ /* memory not released using delete */ cout << "test 2" << endl; Apple *b = new Apple(); a.push_back(b); a.remove(b); cout << "list size is now " << a.size() << endl; list c = list(); cout << "test 3" << endl; c.push_back(Apple(1)); /* deconstructed after copy by value (memcpy like) */ c.push_back(Apple(2)); /* deconstructed after copy by value (memcpy like) */ /* the list implementation will call new... but not call constructor when Apple(2) is pushed; however, delete will be called; since it was copied by value in the last push_back call double deconstructor on object with same data */ c.pop_back(); Apple z(10); /* will remove nothing */ c.remove(z); cout << "test 4" << endl; /* Apple(5) will never deconstruct. It was literally overwritten by Apple(1). */ /* Think memcpy... but not exactly. */ z = Apple(1); /* will remove by matching using the operator== of Apple or default operator== */ c.remove(z); cout << "test 5" << endl; list> d = list>(); d.push_back(unique_ptr(new Apple())); d.pop_back(); /* z deconstructs */ return 0; } 

Preste especial atención a las direcciones de memoria. Puede ver cuáles apuntan a la stack y cuáles apuntan hacia el montón por los rangos.