std :: list :: remove方法调用每个被删除元素的析构函数吗?
std::list<Node *> lst; //.... Node * node = /* get from somewhere pointer on my node */; lst.remove(node);
std :: list :: remove方法调用每个被删除元素的析构函数(和可用内存)吗? 如果是的话,我该如何避免呢?
是的,从容器中移除Foo*
破坏了Foo*
,但不会释放Foo
。 销毁一个原始指针始终是一个无操作。 它不能是任何其他的方式! 让我给你几个原因为什么。
存储类
只有当指针被dynamic地分配时,删除一个指针才有意义,但是当指针variables被销毁时,运行时怎么可能知道是否是这种情况呢? 指针也可以指向静态和自动variables,并删除其中的一个会产生未定义的行为 。
{ 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.) }
未初始化的指针
指针variables是否已经被初始化也是不可能的。 猜猜当你试图删除这样一个指针会发生什么? 答案再次是未定义的行为 。
{ Foo* p; // Has p been properly initialized? // (The answer is NO, but the runtime doesn't know that.) }
dynamic数组
types系统不区分指向单个对象( Foo*
)的指针和指向对象数组的第一个元素(也是Foo*
)的指针。 当一个指针variables被销毁时,运行时不可能通过delete
或者通过delete[]
来判断是否释放指针。 通过错误的forms释放调用未定义的行为 。
{ 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.) }
概要
由于运行时无法对指针进行任何操作,因此销毁指针variables始终是无操作的。 无所事事肯定比导致未定义的行为,因为一个不知情的猜测:-)
忠告
不要使用原始指针,而应考虑使用智能指针作为容器的值types,因为它们在不再需要时负责释放指针。 根据您的需要,使用std::shared_ptr<Foo>
或std::unique_ptr<Foo>
。 如果您的编译器不支持C ++ 0x,请使用boost::shared_ptr<Foo>
。
永远不要重复, 永远不要使用std::auto_ptr<Foo>
作为容器的值types。
它调用list
中每个项目的析构函数 – 但这不是一个Node
对象。 它的一个Node*
。
所以它不删除Node
指针。
那有意义吗?
它调用列表中数据的析构函数。 这意味着, std::list<T>::remove
将调用T
的析构函数(当T
是std::vector
时,这是必须的)。
在你的情况下,它会调用Node*
的析构函数,这是一个无操作。 它不调用node
的析构函数。
是的,尽pipe在这种情况下,Node *没有析构函数。 尽pipe取决于其内部,但各种Node *值可以通过作用域规则来删除或销毁。 如果Node *里面有一些非基本types,析构函数会被调用。
在Node上调用析构函数吗? 不,但“节点”不是列表中的元素types。
至于你的其他问题,你不能。 标准列表容器(实际上是所有的标准容器)都采用其内容的所有权并将其清理。 如果你不希望发生这种情况,标准容器不是一个好的select。
由于您将指针放入std::list
,因此不会在指向的Node
对象上调用析构函数。
如果你想在STL容器中存储堆分配的对象,并删除它们被删除,将它们包装在一个智能指针,如boost::shared_ptr
理解最好的方法是testing每个表单并观察结果。 为了巧妙地将容器对象与你自己的自定义对象一起使用,你需要对这个行为有一个很好的理解。
简而言之,对于typesNode*
既不调用解构器也不调用delete / free; 然而,对于typesNode
解构器将被调用,而考虑删除/免费是列表的实现细节。 意思是,这取决于列表实现是否使用new / malloc。
在unique_ptr<Node>
的情况下,解构器被调用并且调用delete / free将会发生,因为你必须给它一些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; }
请注意内存地址。 你可以知道哪些指向堆栈,哪些指向堆的范围。