人们说,如果您需要一个析构函数,那么您实际上需要一个重载的operator=。
struct longlife{ };
class z
{
z(){};
~z(){ for( auto it=hold.begin();it!=hold.end() ++it ) delete(*it); };
vector<longlife*> hold;
};假设插入到hold上的所有指针都分配了new堆,为什么在这个示例中除了需要解构函数之外,还需要其他什么?
anything else的意思是,
z& operator=( const z&ref )
{
hold = ref.hold;
return *this;
}将:
z a;
a.hold.push_back( heap_item );
z a2;
a2 = a;因为记忆泄漏?有时很难理解为什么三的规则是规则
发布于 2012-10-10 20:53:42
不仅需要赋值操作符,还需要实现副本构造函数。否则,编译器将提供默认实现,这些实现将导致两个副本(在赋值/复制构造之后)包含指向相同longlife实例的指针。然后,两个副本的析构函数都会delete这些实例,从而导致未定义的行为。
z a;
a.hold.push_back( heap_item );
z a2;
a2 = a;a.hold[0]和a2.hold[0]都包含指向同一heap_item的指针;因此在销毁过程中会导致双重删除。
避免实现赋值操作符和复制构造函数的简单方法是使用智能指针来保存vector中的vector实例。
std::vector<std::unique_ptr<longlife>> hold;现在,甚至没有必要为您的类编写析构函数。
对于C++03,您的选项是使用std::tr1::shared_ptr (或boost::shared_ptr)代替unique_ptr或使用boost::ptr_vector (当然,这也是C++11的一个选项)而不是std::vector。
发布于 2012-10-10 20:52:08
因为没有赋值操作符和副本构造函数,最终可能会有多个指向同一个堆项的hold向量,导致破坏时未定义的行为:
z firstZ;
if (somethingIsTrue) {
z otherZ = firstZ;
// play with otherZ...
// now otherZ gets destructed, along with longlife's of the firstZ
}
// now it's time to destroy the firstZ, but its longlife's are long gone!当然,如果使用对象向量或“智能指针”向量,而不是“普通旧”指针的向量,则不会出现此问题。
有关更多信息,请参见http://en.wikipedia.org/wiki/Rule_of_three_%28C++_programming%29。
发布于 2012-10-10 20:51:29
实际上这里会有双自由,而不是内存泄漏。
STL容器存储对象,而不是引用。在您的例子中,object是一个指针。指针被简单地复制。您的行a2 = a;将在向量中复制指针。之后,每个析构函数将释放指针。
双自由比内存泄漏危险得多。它会引起令人讨厌的未定义的行为:
MyStruct *p1 = new MyStruct();
delete p1;
.... do something, wait, etc.
delete p1;同时在另一个线程上:
MyOptherStruct *p2 = new MyOtherStruct();
.... do something, wait, etc.
p2->function();结果可能是内存分配程序将为p2分配与p1使用的值完全相同的值,因为在第一次调用delete p1之后它是空闲的。稍后,第二个delete p1也会变得很好,因为分配程序认为这是为p2分配的合法指针。这个问题只会出现在p2->function();。看一下线程2的代码,完全不可能理解哪里出了问题以及为什么出错。这是非常难调试的,特别是当系统很大的时候。
https://stackoverflow.com/questions/12828091
复制相似问题