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

C++11 interface pure virtual destructor

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;

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

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

Demo

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

我能找到的是:

§12.4 (5.9)

A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.

导致:

§10.4(class 现在是抽象的)

10.4 (2) 说:

A pure virtual function need be defined only if called with, or as if with (12.4), the qualified-id syntax (5.1).

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

这意味着:

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

  • class 现在是抽象的。

变体 1 将允许您拥有 class 的实例。变体 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() 而不是通过接口。也就是说,不清楚您的 目标 是什么纯虚拟析构函数。如果您想避免实例化(即 static class),您可以考虑删除构造函数;如果你想要可以实例化的后代而不是这个class,也许你需要一个在后代中实现的纯虚拟成员函数。

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

现在,空值或默认值 destructor/constructor(I21 和 I22)之间的区别更加模糊,那里没有写太多。推荐的是使用 default,既作为一种新的习语使你的意图更清楚,显然,也给编译器一个优化的机会。引用 msdn

Because of the performance benefits of trivial special member functions, we recommend that you prefer automatically generated special member functions over empty function bodies when you want the default behavior.

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