C++11接口纯虚拟析构函数



UPD。有一个标记表明这是这个问题的重复。但在这个问题中,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;

更新我发现变体1和变体2.1/2.2:之间至少有1个差异

std::is_abstract::value对于变体1是false,对于变体2.1和2.2是true

演示

可能有人能找到2.1和2.2之间的区别吗?

I1和I2*之间的区别,正如您所指出的,是添加= 0使类变得抽象。事实上,当你没有任何其他函数是纯虚拟的时,让析构函数成为纯虚拟的是一个让类抽象的技巧。我说过这是一个技巧,因为如果你想破坏它的任何派生类(在这里你会这样做),那么你仍然需要定义析构函数,无论是空的还是默认的。

现在,空的或默认的析构函数/构造函数(I21和I22)之间的区别要模糊得多,没有太多内容。建议使用default,这既是一个新的习惯用法,可以使您的意图更加清晰,也显然是为了给编译器一个优化的机会。报价msdn

由于琐碎的特殊成员函数的性能优势,当您想要默认行为时,我们建议您更喜欢自动生成的特殊成员功能,而不是空函数体。

除了可能的性能改进之外,两者之间没有明显的区别。= default是从C++11开始的

我能找到的只有:

§12.4(5.9)

析构函数可以声明为虚拟的(10.3)或纯虚拟的(10.4);如果该类的任何对象派生类在程序中创建时,应定义析构函数。如果一个类有一个基类虚拟析构函数,其析构函数(无论是用户声明的还是隐式声明的)都是虚拟的。

导致:

§10.4(该类别现在是抽象的)

10.4(2)说:

纯虚拟函数只有在使用限定id语法(5.1)调用时才需要定义

但是§12.4中关于析构函数的叙述谈到了析构函数总是被当作它们的完全限定名称来调用(为了防止歧义)。

这意味着:

  • 即使是纯虚拟的,也必须定义析构函数,并且

  • 这门课现在是抽象的。

变量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(),而不是通过接口。也就是说,目前还不清楚纯虚拟析构函数的目标是什么。如果您想避免实例化(即静态类),可以考虑删除构造函数;如果您想要可以实例化但不能实例化此类的子体,那么您可能需要一个在子体中实现的纯虚拟成员函数。

最新更新