4

在 C++11 中,多态类(具有virtual成员方法的类)应该/必须有一个virtual析构函数(这样delete基类指针就可以实现预期的效果)。但是,显式声明析构函数会弃用复制构造函数的隐式生成(尽管这可能不会被编译器广泛实现),因此也弃用默认构造函数。因此,对于任何不被弃用的多态类,它必须具有这些成员

virtual ~polymorphic_class() = default;
polymorphic_class() = default;
polymorphic_class(polymorphic_class const&) = default;

明确定义,即使它们是微不足道的。我对么?(这不是很烦人吗?)这背后的逻辑是什么?有没有办法避免这种情况?

4

2 回答 2

2

我对么?

是的,根据ForEveR帖子

有没有办法避免这种情况?

是的。只需为所有多态类实现一个基(类似于ObjectJava 和 D 中的类,它是所有类层次结构的根),只需执行一次:

struct polymorphic {

    polymorphic()                               = default;
    virtual ~polymorphic()                      = default;

    polymorphic(const polymorphic&)             = default;
    polymorphic& operator =(const polymorphic&) = default;

    // Those are not required but they don't harm and are nice for documentation
    polymorphic(polymorphic&&)                  = default;
    polymorphic& operator =(polymorphic&&)      = default;
};

然后,任何公开派生的类polymorphic都将具有隐式声明和定义(默认)的虚拟析构函数,除非您自己声明。

class my_polymorphic_class : public polymorphic {
};

static_assert(std::is_default_constructible<my_polymorphic_class>::value, "");
static_assert(std::is_copy_constructible   <my_polymorphic_class>::value, "");
static_assert(std::is_copy_assignable      <my_polymorphic_class>::value, "");
static_assert(std::is_move_constructible   <my_polymorphic_class>::value, "");
static_assert(std::is_move_assignable      <my_polymorphic_class>::value, "");

这背后的逻辑是什么?

我不能确定。以下只是猜测。

在 C++98/03 中,三法则说,如果一个类需要用户定义复制构造函数、复制赋值运算符或析构函数,那么它可能需要用户定义这三个。

遵守三法则是一个很好的做法,但这只是一个指导方针。标准并不强制它。为什么不?我的猜测是人们只有在标准发布后才意识到这个规则。

C++11 引入了移动构造函数和移动赋值运算符,将三法则变为五法则。事后看来,委员会希望执行五法则。这个想法是:如果五个特殊函数中的任何一个是用户声明的,那么其他的,但析构函数,将不会被隐式默认。

然而,委员会并不想通过强制执行此规则来破坏几乎所有 C++98/03 代码,然后决定仅部分执行:

  1. 如果移动构造函数或移动赋值运算符是用户声明的,则将删除除析构函数之外的其他特殊函数。

  2. 如果用户声明了五个特殊函数中的任何一个,则不会隐式声明移动构造函数和移动赋值运算符。

在 C++98/03 格式良好的代码的情况下,移动构造函数和移动赋值运算符都不会被用户声明,规则 1 不适用。因此,当使用符合 C++11 的编译器进行编译时,C++98/03 格式良好的代码不会因为此规则而无法编译。(如果是这样,那是出于其他一些原因。)

此外,根据规则 2,移动构造函数和移动赋值运算符没有隐式声明。这也不会破坏 C++98/03 格式良好的代码,因为他们无论如何都不会期望声明移动操作。

在 OP 中提到并在ForEveR帖子中引用的弃用表明未来标准可能会执行五规则。做好准备!

于 2013-11-16T12:59:55.213 回答
1

你是对的,未来的标准应该是这样,但现在它只是被弃用了,所以当析构函数现在是虚拟的时,每个编译器都应该支持隐式声明的复制构造函数。

n3376 12.8/7

如果类定义没有显式声明复制构造函数,则隐式声明。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数定义为已删除;否则,它被定义为默认值(8.4)。如果类具有 用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况。

在我看来,您无法为此采取任何解决方法。

于 2013-11-15T09:31:47.393 回答