std::list::remove 方法是否调用每个被删除元素的析构函数?

问题描述 投票:0回答:6

我有代码:

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

std::list::remove
方法是否调用每个删除元素的析构函数(和释放内存)?如果是这样,我该如何避免?

c++ list stl destructor c++-faq
6个回答
49
投票

是的,从容器中取出

Foo*
会破坏
Foo*
,但不会释放
Foo
。销毁原始指针“始终”是无操作的。不可能有其他方式!让我给你几个理由。 存储类

只有当指针对象实际上是动态分配的时候,删除指针才有意义,但是运行时怎么可能知道指针变量被销毁时是否是这种情况呢?指针还可以指向静态和自动变量,删除其中之一会产生

未定义的行为

{ 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.) }

悬空指针

无法确定被指向者过去是否已经被释放。两次删除同一个指针会产生

未定义的行为

。 (第一次删除后,它变成了悬空指针。) { 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.) }

未初始化的指针

根本无法检测指针变量是否已经初始化。猜猜当你尝试删除这样的指针时会发生什么?再次,答案是

未定义的行为

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

动态数组

类型系统不区分指向单个对象的指针(

Foo*

)和指向对象数组的第一个元素的指针(也

Foo*
)。当指针变量被销毁时,运行时不可能确定是通过
delete
还是通过
delete[]
释放指针。通过错误的形式释放会调用
未定义的行为
{ 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.) }

总结

由于运行时无法对指针对象执行任何明智的操作,因此销毁指针变量是

始终

无操作。什么都不做肯定比由于不知情的猜测而导致未定义的行为要好:-) 建议

考虑使用智能指针作为容器的值类型,而不是原始指针,因为它们负责在不再需要指针时释放指针。根据您的需要,使用

std::shared_ptr<Foo>

std::unique_ptr<Foo>
。如果您的编译器尚不支持 C++0x,请使用
boost::shared_ptr<Foo>

从不

,我再说一遍,从不使用std::auto_ptr<Foo>作为容器的值类型。

    


12
投票
list

中每个项目的析构函数——但这不是

Node
对象。 这是一个
Node*
所以它不会删除 

Node

指针。

这有道理吗?


7
投票
std::list<T>::remove

将调用

T
的析构函数(当
T
类似于
std::vector
时,这是必需的)。

在你的例子中,它会调用

Node*

的析构函数,这是一个空操作。它不会调用

node
的析构函数。
    


3
投票

Node 上是否调用了析构函数? 不,但“Node”不是列表中的元素类型。

至于你的另一个问题,你不能。 标准列表容器(实际上所有标准容器)采用其内容的所有权并将其清理。 如果您不希望这种情况发生,标准容器不是一个好的选择。


0
投票
std::list

中,因此不会在指向的

Node
对象上调用析构函数。

如果您想将堆分配的对象存储在 STL 容器中并在删除时销毁它们,请将它们包装在智能指针中,如

boost::shared_ptr

    


0
投票

简而言之,对于类型

Node*

,既不调用解构函数,也不调用删除/释放;然而,对于

Node
类型,将调用解构函数,同时考虑删除/释放是列表的实现细节。意思是,这取决于列表实现是否使用 new/malloc。

unique_ptr<Node>

的情况下,会调用解构函数,并且会调用删除/释放,因为您必须给它由

new
分配的东西。

#include <iostream> #include <list> #include <memory> 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<Apple*> a = list<Apple*>(); /* 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<Apple> c = list<Apple>(); 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<unique_ptr<Apple>> d = list<unique_ptr<Apple>>(); d.push_back(unique_ptr<Apple>(new Apple())); d.pop_back(); /* z deconstructs */ return 0; }

特别注意内存地址。您可以通过范围判断哪些指向堆栈,哪些指向堆。

© www.soinside.com 2019 - 2024. All rights reserved.