从赋值运算符调用析构函数会产生什么意外后果吗?

时间:2015-01-23 15:42:24

标签: c++ destructor assignment-operator

例如:

class Foo : public Bar
{
    ~Foo()
    {
        // Do some complicated stuff
    }

    Foo &operator=(const Foo &rhs)
    {
        if (&rhs != this)
        {
            ~Foo(); // Is this safe?

            // Do more stuff
        }
    }
}

在继承和其他类似的事情上明确调用析构函数会产生什么意外后果吗?

有没有理由将析构函数代码抽象为void destruct()函数并调用它?

2 个答案:

答案 0 :(得分:5)

在最简单的情况下调用析构函数是一个坏主意,而在代码变得稍微复杂的那一刻,调用析构函数是一个可怕的。

最简单的情况是:

class Something {
public:
    Something(const Something& o);
    ~Something();
    Something& operator =(const Something& o) {
        if (this != &o) {
            // this looks like a simple way of implementing assignment
            this->~Something();
            new (this) Something(o); // invoke copy constructor
        }
        return *this;
    }
};

这是一个坏主意。如果复制构造函数抛出,则会留下原始内存 - 那里没有对象。只有在赋值运算符之外,没有人注意到。

如果继承发挥作用,情况会变得更糟。让我们说Something实际上是一个带有虚析构函数的基类。派生类的功能都是使用默认值实现的。在这种情况下,派生类的赋值运算符将执行以下操作:

  1. 它将调用自己的基本版本(您的赋值运算符)。
  2. 您的赋值运算符调用析构函数。这是一个虚拟电话。
  3. 派生的析构函数会破坏派生类的成员。
  4. 基础析构函数会销毁基类的成员。
  5. 您的赋值运算符调用复制构造函数。这不是虚拟的;它实际上构造了一个基础对象。 (如果基类不是抽象的。如果是,则代码不会编译。)现在,您已经用基础对象替换了派生对象。
  6. 复制构造函数构造基础成员。
  7. 派生赋值运算符对派生类的成员进行成员分配。哪些已被破坏而未重新创建。
  8. 此时,你有多个UB的实例相互堆积在一堆光辉的混乱之中。

    所以是的。别这么做。

答案 1 :(得分:0)

绝对不是。

void f()
{
    Foo foo, fee;
    foo = fee; <-- a first foo.~Foo will be called here !
} <-- a second foo.~Foo will be called here and fee.~Foo as well !

正如您所看到的,您有3次调用析构函数而不是预期的2次调用。

你应该 NOT 在构造函数或非静态方法中使用* self- *析构函数。