为什么默认的赋值操作符没有先调用析构函数?
所以在下面的例子中,我们导致类Foo
用*this = Foo()
代替。我很高兴我刚刚测试过这个,因为在这种情况下,旧的Foo
的析构函数没有被调用。我想这是因为默认赋值运算符只是使用memcpy
......但是作为语言设计问题...为什么不让默认赋值运算符首先销毁指定对象以防止意外?为什么默认的赋值操作符没有先调用析构函数?
#include <iostream>
using namespace std;
class MustBeDestroyed //(for some reason not shown here)
{
public:
int i;
MustBeDestroyed(int i) : i(i) {}
~MustBeDestroyed() { cout << "destroyed contained class " << i << endl; }
};
class Foo
{
public:
MustBeDestroyed x;
Foo(int y) : x(y) {}
void replace_myself(int y) { Foo f(y); *this=f; }
void print() { cout << "This is outer/inner class " << x.i << endl; }
~Foo() { cout << "destroyed outer class " << x.i << endl; }
};
int main()
{
Foo a(1);
a.print();
a.replace_myself(2);
a.print();
return 0;
}
规则为什么会指派调用析构函数?它的确如它所说的那样做:它调用赋值运算符。编译器生成的赋值运算符很简单:将所有成员从旧对象分配给新对象(使用它们的赋值操作)。没有更多,没有更多。这正是着名的rule of three的原因。
现在至于为什么它不调用析构函数:这将结束对象的生命周期。尽管在理论上可能在旧的物体上构建一个新的物体,但这种方法在异常(look at this question for more about that)中通常是不正确的,因此它在一般情况下不能使用。此外,如果它采用了你提出的方法,它不会为成员调用赋值运算符,而是调用析构函数/拷贝构造函数。这意味着自定义分配行为(实际上并不需要与复制行为相同)不会受到尊重。
如果被覆盖的值包含一个ptr到堆中的数据,分配将首先调用析构函数以避免内存泄漏。如果你简单地覆盖ptr,那么你会泄漏内存。因此,在这种情况下内置的赋值运算符应该被覆盖(因为内置的将会泄漏)。 – wcochran 2016-02-09 04:44:15
由于第一销毁对象将结束寿命。然后你将不得不调用一个构造函数来启动新对象的生命周期。但是operator =的行为不是破坏当前对象并创建另一个对象,而是为现有对象分配一个新值。
基本上,你违反了3
什么意外?赋值就是这样 - 为旧值分配一个新值。 – 2013-05-02 14:47:06
默认赋值运算符不使用memcpy(尽管编译器优化可能会导致这种情况)。这是一项成员任务。 – huskerchad 2013-05-02 14:48:49
'int i,j;我= 5; j = i;'你是不是说我期望不再使用'i'是明智的?我不同意。 – Fiktik 2013-05-02 14:51:38