在C ++ 11中,多态类(一个具有virtual
成员方法)应该/必须有一个virtual
析构函数(以便基类指针上的delete
执行预期)。但是,声明析构函数显式地弃用了复制构造函数的隐式生成(尽管编译器可能没有广泛实现),因此默认构造函数也是如此。因此,要使任何多态类不被弃用,它必须具有这些成员
virtual ~polymorphic_class() = default;
polymorphic_class() = default;
polymorphic_class(polymorphic_class const&) = default;
显式定义,即使它们是微不足道的。我对么? (这不是很烦人吗?)这背后的逻辑是什么?有没有办法避免这种情况?
答案 0 :(得分:1)
你是对的,将来标准应该是真的,但是现在它只是被弃用了,所以当析构函数现在是虚拟的时候,每个编译器都应该支持隐式声明的复制构造函数。
n3376 12.8 / 7
如果类定义没有显式声明复制构造函数,则会隐式声明一个。如果上课 definition声明一个move构造函数或move赋值运算符,隐式声明的复制构造函数 被定义为删除; 否则,它被定义为默认值(8.4)。如果类具有,则不推荐使用后一种情况 用户声明的复制赋值运算符或用户声明的析构函数。
在我看来,你无法为此做任何解决方法。
答案 1 :(得分:1)
我说错了吗?
有什么办法可以避免吗?
是。通过为所有多态类实现一个基础(类似于Java中的类Object
和所有类层次结构的根)来执行此操作一次:
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_assingable <my_polymorphic_class>::value, "");
static_assert(std::is_move_constructible <my_polymorphic_class>::value, "");
static_assert(std::is_move_assingable <my_polymorphic_class>::value, "");
这背后的逻辑是什么?
我无法确定。以下是猜测。
在C ++ 98/03中,Rule of Three表示如果一个类需要复制构造函数,复制赋值运算符或用户定义的析构函数,那么它可能需要三个用户定义
遵守三法则是一种很好的做法,但这只不过是一个指导原则。标准不强迫它。为什么不?我的猜测是,只有在标准发布后人们才意识到这一规则。
C ++ 11引入了移动构造函数和移动赋值运算符,将三元规则转化为五元规则。事后看来,委员会希望执行五法则。这个想法是:如果五个特殊函数中的任何一个是用户声明的,那么其他的,但是析构函数,不会被隐式默认。
但是,委员会不想通过强制执行此规则来破坏几乎每个C ++ 98/03代码,然后决定只部分执行:
如果移动构造函数或移动赋值运算符是用户声明的,则其他特殊函数(但析构函数)将被删除。
如果五个特殊函数中的任何一个是用户声明的,则不会隐式声明移动构造函数和移动赋值运算符。
对于C ++ 98/03格式良好的代码,移动构造函数和移动赋值运算符都不是用户声明的,规则1不适用。因此,当使用符合C ++ 11的编译器编译时,C ++ 98/03格式良好的代码不会因此规则而无法编译。 (如果确实如此,那是出于其他一些原因。)
此外,根据规则2,不会隐式声明移动构造函数和移动赋值运算符。这不会破坏C ++ 98/03良好的代码,因为他们从未预料到移动操作的声明无论如何。