C ++ 11虚拟析构函数和自动生成移动特殊函数

时间:2015-03-26 22:20:47

标签: c++ c++11 inheritance move-semantics

C ++ 11中自动生成特殊移动函数(构造函数和赋值运算符)的规则指定不能声明析构函数。据推测,如果你需要在破坏方面做一些特别的事情,那么这一举动可能并不安全。

但是,对于多态中适当的析构函数调用,必须声明一个基类'析构函数为虚拟(否则通过其基类的指针删除子类的实例将无法正确链接析构函数)。

我假设,即使是空的析构函数也会阻止编译器自动生成特殊的移动函数。如:

class Base {
    virtual ~Base() { }
};

但是,您可以默认析构函数,如:

class Base {
    virtual ~Base() = default;
}

问题1:这是否允许编译器自动生成特殊的移动函数?

然而,显式默认析构函数存在问题。至少在GCC 4.8.2的情况下,签名被隐式地改为noexcept。如:

class Base {
    virtual ~Base() = default; // compiler changes to:
    // virtual ~Base() noexcept;
}

虽然我在析构函数中对noexcept没有任何问题,但这会破坏以下"客户端"代码:

class Sub : public Base {
    virtual ~Sub(); // this declaration is now "looser" because of no noexcept
}

所以问题2更重要的是:有没有办法允许在C ++ 11中自动生成特殊的移动函数,并允许正确的析构函数链接到子类(如上所述),所有这些都不会破坏子类(&# 34;客户")代码?

1 个答案:

答案 0 :(得分:18)

  1. 不,默认的析构函数仍然被认为是用户定义的,因此它将阻止生成移动操作。同时声明移动操作default - ed以使编译器生成它们。

  2. 您只需将移动操作声明为default - 在基类中编辑。在派生类中,析构函数不再是用户定义的(除非您明确说明),因此不会删除移动操作。

  3. 所以我要做的是以下内容:

    class Base
    {
        virtual ~Base() = default;
        Base(Base&&) = default;
        Base& operator=(Base&&) = default;
        // probably need to think about copy operations also, as the move disables them
        Base(const Base&) = default;
        Base& operator=(const Base&) = default;
    };
    

    我强烈推荐那些对移动语义贡献最大的人的演讲:http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014

    或者,如果你可以开始,你应该阅读来自Scott Meyers的优秀书籍 Effective Modern的项目17:理解特殊成员函数生成 C ++ 。这个问题得到了很好的解释。

    PS:我认为你应该多考虑一下你的基类。大多数情况下,您应该使用抽象类,因此不需要复制/移动它们的实例。

    PSS:我认为默认情况下,析构函数在C ++ 11/14中标记为noexcept,因此不明确指定它不会导致任何问题:

      

    继承构造函数和隐式声明的默认值   构造函数,复制构造函数,移动构造函数,析构函数,   复制赋值运算符,移动赋值运算符都是   除非需要调用函数,否则默认情况下为noexcept(true)   这是noexcept(false),在这种情况下这些函数是   noexcept(假)。