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

Posted

技术标签:

【中文标题】默认的虚拟析构函数是不是会阻止编译器生成的移动操作?【英文标题】:Does a default virtual destructor prevent compiler-generated move operations?默认的虚拟析构函数是否会阻止编译器生成的移动操作? 【发布时间】:2016-03-01 15:14:57 【问题描述】:

受帖子 Why does destructor disable generation of implicit move methods? 的启发,我想知道默认虚拟析构函数是否也是如此,例如

class WidgetBase // Base class of all widgets

    public:
        virtual ~WidgetBase() = default;
        // ...
;

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

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

【问题讨论】:

【参考方案1】:

不是解决方案,而是可能的解决方法之一。 您可以从只有默认虚拟析构函数的类继承所有类。

我使用 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 会导致泄漏!这与他们在存储删除器方面的差异有关。

【讨论】:

我一直在试图弄清楚你的第一个建议......所以我认为它说:基类不会得到移动 c'tor,但派生类会(因为它没有自己的虚拟 d'tor)? - 感谢您的确认 :)【参考方案2】:

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

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

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

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

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

WidgetBase(WidgetBase&&) = default;

请注意,这会将复制构造函数定义为delete,因此您也需要default

WidgetBase(const WidgetBase&) = default;

复制和移动赋值运算符的规则也非常相似,所以如果你需要它们,你必须default它们。

【讨论】:

以上是关于默认的虚拟析构函数是不是会阻止编译器生成的移动操作?的主要内容,如果未能解决你的问题,请参考以下文章

C++类的默认函数

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

为啥析构函数会禁用隐式移动方法的生成?

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

编译器何时不生成移动构造函数和移动赋值?

C++析构拷贝赋值移动拷贝函数的几个知识点(不全)