默认虚析构函数是否会阻止编译器生成的移动操作?

38

受到帖子“为什么析构函数禁用隐式移动方法生成?”的启发,我想知道默认虚析构函数是否也是如此。

class WidgetBase // Base class of all widgets
{
    public:
        virtual ~WidgetBase() = default;
        // ...
};

由于这个类旨在作为小部件层次结构的基类,我必须定义其析构函数为虚函数,以避免在使用基类指针时出现内存泄漏和未定义行为。另一方面,我不想阻止编译器自动生成移动操作。

默认虚析构函数会阻止编译器生成移动操作吗?

2个回答

32

是的,声明任何析构函数都将阻止移动构造函数的隐式声明。

N3337 [class.copy]/9:如果类X的定义没有显式声明移动构造函数,则仅当

  • X没有用户声明的复制构造函数,
  • X没有用户声明的复制赋值运算符,
  • X没有用户声明的移动赋值运算符,
  • X没有用户声明的析构函数,且
  • 移动构造函数不会被隐式定义为已删除。

声明并将析构函数定义为default也算作用户声明

您需要自己声明并将移动构造函数定义为default

WidgetBase(WidgetBase&&) = default;

请注意,这将反过来定义复制构造函数为delete,因此您需要default该构造函数:

WidgetBase(const WidgetBase&) = default;

复制和移动赋值运算符的规则也非常相似,因此如果您想使用它们,就必须default它们。


显然,使用者定义的虚析构函数不会删除移动操作。请参见:http://coliru.stacked-crooked.com/a/b95b6bb23630755f - dwto
显然,用户定义的虚析构函数不会删除移动操作。请参见:http://coliru.stacked-crooked.com/a/b95b6bb23630755f - undefined

0

这不是解决方案,但是其中一种可能的解决方法。 您可以从一个只有默认虚析构函数的类继承所有类。

我使用GCC 9和Apple的Clang++进行了检查,并使用-std=c++17:它们都为继承下面类的类生成移动构造函数。

class Object {
public:
    virtual ~Object() = default;
};

下面的类确实会有一个移动构造函数。
class Child : public Object {
public:
    Child(std::string data) : data(data) {
    }

private:
    std::string data;

};

另一个可能但风险较高的解决方法是根本不声明虚析构函数。这会引入以下风险:

  • 所有对象必须始终由知道其确切类型的人销毁。在设计良好的C++代码中,这并不是什么大问题。
  • 当此类对象存储在像std::vectorstd::list这样的容器中时,必须始终使用std::shared_ptr进行包装。std::unique_ptr会导致泄漏!这与它们存储删除器的差异有关。

我已经尝试了一段时间来理解你的第一个建议...所以我认为它的意思是:基类不会得到移动构造函数,但派生类会(因为它没有自己的虚拟析构函数)?- 谢谢你的确认 :) - code_fodder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接