C++11接口纯虚析构函数
Posted
技术标签:
【中文标题】C++11接口纯虚析构函数【英文标题】:C++11 interface pure virtual destructor 【发布时间】:2016-10-13 03:43:18 【问题描述】: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
。
Demo
可能有人能发现 2.1 和 2.2 之间的区别吗?
【问题讨论】:
关于虚拟继承的问题是什么? 【参考方案1】:正如您所指出的,I1 和 I2* 之间的区别在于添加 = 0
会使类抽象。事实上,当您没有任何其他函数成为纯虚拟时,使析构函数成为纯虚拟是一种使类抽象的技巧。我说这是一个技巧,因为如果你想破坏它的任何派生类(在这里你会),析构函数不能未定义,那么你仍然需要定义析构函数,无论是空的还是默认的。
现在空的或默认的析构函数/构造函数(I21 和 I22)之间的区别更加模糊,那里没有太多的文字。推荐的方法是使用default
,这既是一种新的习惯用法,可以使您的意图更加清晰,而且显然是为了给编译器一个优化的机会。引用msdn
由于琐碎的特殊成员函数的性能优势,我们建议您在需要默认行为时更喜欢自动生成的特殊成员函数而不是空函数体。
除了这种可能的性能改进之外,两者之间没有明显的差异。 = default
是从 C++11 开始的方式。
【讨论】:
【参考方案2】:我只能找到:
§12.4 (5.9)
析构函数可以声明为虚拟(10.3)或纯虚拟(10.4);如果该类的任何对象或任何 派生类在程序中创建,必须定义析构函数。如果一个类有一个基类 虚拟析构函数,它的析构函数(无论是用户声明的还是隐式声明的)都是虚拟的。
导致:
§10.4(该类现在是抽象的)
10.4 (2) 说:
纯虚函数仅在使用或如同使用 (12.4) 限定 ID 语法 (5.1) 调用时才需要定义。
但是第 12.4 节中关于析构函数的叙述谈到析构函数总是被称为完全限定名称(为了防止歧义)。
这意味着:
必须定义析构函数,即使是纯虚函数,并且
这个类现在是抽象的。
【讨论】:
次要注意:在这些段落中没有必须定义析构函数的内容,恰恰相反。如果您想要一个“静态类”(每个成员/成员函数都是静态的类),则不需要析构函数。 @lorro 前提是您不创建该类的对象,是的。你说得对。但有人想知道,为什么在这种情况下你会声明一个析构函数......【参考方案3】:变体 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() 而不是通过接口。也就是说,不清楚您的目标是什么纯虚拟析构函数。如果您想避免实例化(即静态类),您可以考虑删除构造函数;如果您想要可以实例化的后代而不是此类,那么您可能需要一个在后代中实现的纯虚成员函数。
【讨论】:
当然在所有变体中(包括变体 1)我的意思是该类将包含其他纯虚拟成员...以上是关于C++11接口纯虚析构函数的主要内容,如果未能解决你的问题,请参考以下文章
C++中的各种“虚“-- 虚函数纯虚函数虚继承虚基类虚析构纯虚析构抽象类讲解