为什么默认的赋值操作符没有先调用析构函数?

为什么默认的赋值操作符没有先调用析构函数?

问题描述:

所以在下面的例子中,我们导致类Foo*this = Foo()代替。我很高兴我刚刚测试过这个,因为在这种情况下,旧的Foo的析构函数没有被调用。我想这是因为默认赋值运算符只是使用memcpy ......但是作为语言设计问题...为什么不让默认赋值运算符首先销毁指定对象以防止意外?为什么默认的赋值操作符没有先调用析构函数?

http://codepad.org/9WCo6yZ5

#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; 
} 
+2

什么意外?赋值就是这样 - 为旧值分配一个新值。 – 2013-05-02 14:47:06

+1

默认赋值运算符不使用memcpy(尽管编译器优化可能会导致这种情况)。这是一项成员任务。 – huskerchad 2013-05-02 14:48:49

+1

'int i,j;我= 5; j = i;'你是不是说我期望不再使用'i'是明智的?我不同意。 – Fiktik 2013-05-02 14:51:38

规则为什么会指派调用析构函数?它的确如它所说的那样做:它调用赋值运算符。编译器生成的赋值运算符很简单:将所有成员从旧对象分配给新对象(使用它们的赋值操作)。没有更多,没有更多。这正是着名的rule of three的原因。

现在至于为什么它不调用析构函数:这将结束对象的生命周期。尽管在理论上可能在旧的物体上构建一个新的物体,但这种方法在异常(look at this question for more about that)中通常是不正确的,因此它在一般情况下不能使用。此外,如果它采用了你提出的方法,它不会为成员调用赋值运算符,而是调用析构函数/拷贝构造函数。这意味着自定义分配行为(实际上并不需要与复制行为相同)不会受到尊重。

+1

如果被覆盖的值包含一个ptr到堆中的数据,分配将首先调用析构函数以避免内存泄漏。如果你简单地覆盖ptr,那么你会泄漏内存。因此,在这种情况下内置的赋值运算符应该被覆盖(因为内置的将会泄漏)。 – wcochran 2016-02-09 04:44:15

由于第一销毁对象将结束寿命。然后你将不得不调用一个构造函数来启动新对象的生命周期。但是operator =的行为不是破坏当前对象并创建另一个对象,而是为现有对象分配一个新值。

基本上,你违反了3