以下代码片段是否泄漏?如果没有,那么在foobar()中构造的两个对象在哪里被破坏?
class B
{
int* mpI;
public:
B() { mpI = new int; }
~B() { delete mpI; }
};
void foobar()
{
B b;
b = B(); // causes construction
b = B(); // causes construction
}
答案 0 :(得分:7)
默认的复制赋值运算符执行成员方复制。
所以在你的情况下:
{
B b; // default construction.
b = B(); // temporary is default-contructed, allocating again
// copy-assignment copies b.mpI = temp.mpI
// b's original pointer is lost, memory is leaked.
// temporary is destroyed, calling dtor on temp, which also frees
// b's pointer, since they both pointed to the same place.
// b now has an invalid pointer.
b = B(); // same process as above
// at end of scope, b's dtor is called on a deleted pointer, chaos ensues.
}
有关详细信息,请参阅Effective C ++第2版中的第11项。
答案 1 :(得分:4)
是的,这确实泄漏了。编译器会自动提供额外的方法,因为您尚未定义它。它生成的代码等同于:
B & B::operator=(const B & other)
{
mpI = other.mpI;
return *this;
}
这意味着会发生以下事情:
B b; // b.mpI = heap_object_1
B temp1; // temporary object, temp1.mpI = heap_object_2
b = temp1; // b.mpI = temp1.mpI = heap_object_2; heap_object_1 is leaked;
~temp1(); // delete heap_object_2; b.mpI = temp1.mpI = invalid heap pointer!
B temp2; // temporary object, temp1.mpI = heap_object_3
b = temp1; // b.mpI = temp2.mpI = heap_object_3;
~temp1(); // delete heap_object_3; b.mpI = temp2.mpI = invalid heap pointer!
~b(); // delete b.mpI; but b.mpI is invalid, UNDEFINED BEHAVIOR!
这显然很糟糕。在您违反rule of three的任何情况下都可能发生这种情况。您已经定义了一个非平凡的析构函数和一个复制构造函数。但是,您尚未定义副本分配。三条规则是,如果你定义上述任何一个,你应该总是定义所有三个。
相反,请执行以下操作:
class B
{
int* mpI;
public:
B() { mpI = new int; }
B(const B & other){ mpI = new int; *mpi = *(other.mpI); }
~B() { delete mpI; }
B & operator=(const B & other) { *mpI = *(other.mpI); return *this; }
};
void foobar()
{
B b;
b = B(); // causes construction
b = B(); // causes construction
}
答案 2 :(得分:3)
你正在建造三个物体,所有物体都将被毁坏。问题是默认的复制赋值运算符将执行浅复制。这意味着指针被复制,这导致它被删除多次。这会导致未定义的行为。
这就是rule of 3背后的原因。你有一个析构函数,但没有其他两个。您需要实现复制构造函数和复制赋值运算符,这两者都应该执行深层复制。这意味着分配一个新的int,将值复制过来。
B(const B& other) : mpI(new int(*other.mpI)) {
}
B& operator = (const B &other) {
if (this != &other)
{
int *temp = new int(*other.mpI);
delete mpI;
mpI = temp;
}
return *this;
}
答案 3 :(得分:0)
正如多次指出的那样,你违反了三条规则。只是为了添加链接,对堆栈溢出有一个很好的讨论:What is The Rule of Three?
答案 4 :(得分:0)
只是提供一种不同的方法来解决我最初发布的代码问题,我想我可以将指针保留在B类中,但是要把内存管理拿出来。然后我不需要自定义析构函数,所以我没有违反3的规则...
class B
{
int* mpI;
public:
B() {}
B(int* p) { mpI = p; }
~B() {}
};
void foobar()
{
int* pI = new int;
int* pJ = new int;
B b; // causes construction
b = B(pI); // causes construction
b = B(pJ); // causes construction
delete pI;
delete pJ;
}