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

时间:2015-11-27 12:04:29

标签: c++ c++11 move-semantics virtual-destructor

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

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

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

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

2 个答案:

答案 0 :(得分:25)

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

  

N3337 [class.copy]/9:如果类X的定义没有显式声明一个移动构造函数,那么将隐式声明一个   当且仅当

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

声明析构函数并将其定义为default计为用户声明的

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

WidgetBase(WidgetBase&&) = default;

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

WidgetBase(const WidgetBase&) = default;

复制和移动赋值运算符的规则也非常相似,因此如果需要,您必须default

答案 1 :(得分:0)

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

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

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

下面的类确实将具有move构造函数。

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会导致泄漏!这与它们与存储删除器的区别有关。