UPD 即可。有一个标记表明它是this question的副本。但在那个问题中,OP问如何使用default
来定义纯虚析构函数。这个问题是关于有什么区别。
在C ++(最新标准,如果可能)中,使用空体实现定义纯虚拟析构函数与仅仅空体(或默认值)之间的真正区别是什么?
变式1:
class I1 {
public:
virtual ~I1() {}
};
变式2.1:
class I21 {
public:
virtual ~I21() = 0;
};
I21::~I21() {}
变式2.2:
class I22 {
public:
virtual ~I22() = 0;
};
I22::~I22() = default;
更新我发现Variant 1和Variants 2.1 / 2.2之间至少有1个区别:
std::is_abstract::value
对于变体1是false
,对于变体2.1和2.2是true
。
可能有人能找到2.1和2.2之间的差异吗?
答案 0 :(得分:7)
正如您所指出的,I1和I2 *之间的区别在于添加= 0
会使类抽象化。实际上,当你没有任何其他函数是纯虚拟的时候,使析构函数变为纯虚拟是一个技巧来使类抽象。我说这是一个技巧,因为如果你想要破坏析构函数的任何派生类(并且你将会这样做),析构函数就不能保持未定义,那么你仍然需要定义析构函数,无论是空的还是默认的。
现在,空的或默认的析构函数/构造函数(I21和I22)之间的区别更加模糊,没有太多的写入。建议使用default
作为一种新的习惯用法,使你的意图更清晰,显然,给编译器一个优化的机会。引用msdn
由于普通特殊成员函数的性能优势,我们建议您在需要默认行为时更喜欢在空函数体上自动生成特殊成员函数。
除了这种可能的性能改进之外,两者之间没有明显的差异。 = default
是从C ++ 11开始的方式。
答案 1 :(得分:4)
我能找到的只有:
§12.4(5.9)
析构函数可以声明为虚拟(10.3)或纯虚拟(10.4);如果该类的任何对象或任何 派生类是在程序中创建的,应该定义析构函数。如果一个类有一个基类 虚析构函数,它的析构函数(无论是用户还是隐式声明)都是虚拟的。
导致:
§10.4(该类现在是抽象的)
10.4(2)说:
只有在使用(12.4)qualified-id语法(5.1)调用时,才需要定义纯虚函数。
但是§12.4中关于析构函数的叙述谈到了析构函数总是被称为完全限定名称(为了防止歧义)。
这意味着:
必须定义析构函数,即使是纯虚拟的,也必须
这个班级现在是抽象的。
答案 2 :(得分:1)
变体1将允许您拥有该类的实例。变体2.1,2.2不允许实例,但允许后代的实例。例如,这可以工作(并且能够让许多人感到困惑),而删除标记的行会使编译失败:
class I21 {
public:
virtual ~I21() = 0;
};
I21::~I21() {} // remove this and it'll not compile
class I22 : public I21
{
public:
virtual ~I22() {}
};
int main() {
I22 i;
return 0;
}
背后的原因,析构函数链直接调用I21 ::〜I21()而不是通过接口。也就是说,目前还不清楚你的目标与纯虚拟析构器的关系。如果您想避免实例化(即静态类),您可以考虑删除构造函数;如果你想要可以实例化而不是这个类的后代,也许你需要一个在后代中实现的纯虚拟成员函数。