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

Posted

技术标签:

【中文标题】C++11 虚拟析构函数和移动特殊函数的自动生成【英文标题】:C++11 virtual destructors and auto generation of move special functions 【发布时间】:2015-05-31 03:33:03 【问题描述】:

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 中自动生成特殊移动函数并允许适当的析构函数链接到子类(如上所述),所有这些都不会破坏子类(“客户端") 代码?

【问题讨论】:

为什么~Sub 更宽松?没有异常规范的析构函数默认为noexcept 这个问题似乎定义得很好,但出于兴趣,您是否有一个示例说明为什么需要这样做?这似乎是值和引用语义的奇怪混合。在我看来,在您想要使用移动(甚至复制)的情况下,您不想要多态行为。更重要的是,默认复制或移动绝对不会有任何多态行为。 @tahsmith 的目标是重做基类,使它们及其子类可以利用移动语义而不会破坏子类。 @Pradhan 当我尝试这样做时,GCC 产生了关于更宽松定义的错误。 @Pradhan 顺便说一句,这不正确,请参见此处:***.com/questions/11497252/default-destructor-nothrow 【参考方案1】:

    不,默认析构函数仍被认为是用户定义的,因此它将阻止生成移动操作。还要声明移动操作default-ed 让编译器生成它们。

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

所以我要做的是:

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),在这种情况下,这些函数是 没有例外(假)。

【讨论】:

谢谢,我认为这解决了问题(并回答了问题)。当我想出这个问题时,我实际上正在阅读 Scott 书中的第 17 条 :) @stephelton 很高兴它有帮助。关于“loser nothrow”规范,我想我之前碰到过这个(g++4.8.x),这是一个编译器问题,会尝试找到链接并在这里发布 @stephelton 找到了,请看这里:***.com/questions/11497252/default-destructor-nothrow

以上是关于C++11 虚拟析构函数和移动特殊函数的自动生成的主要内容,如果未能解决你的问题,请参考以下文章

如何在不破坏移动和复制构造函数的情况下声明虚拟析构函数

[C++11 类的改进] --- 继承控制:=default和=delete

11.7 C++析构函数

11.7 C++析构函数

11.7 C++析构函数

[C++11 类的改进] --- 继承控制:=default和=delete