私有派生析构函数

Private Derived Destructor

当我尝试以多态方式删除派生对象时(即:基class具有public虚拟析构函数)为什么派生class private destructor还在被调用?为什么范围解析 private 在这里不起作用。

class Base
{
protected:
    Base() { cout << "Base constructor.\n"; }
public:
    virtual ~Base() { cout << "Base destructor.\n"; }
};

class Derived :public Base
{
public:
    Derived() { cout << "Derived constructor.\n"; }
private:
   ~Derived() { cout << "Derived destructor.\n"; }
};

int main()
{
    Base *p = new Derived();
    delete p;
}

输出:

Base constructor.
Derived constructor.
Derived destructor.
Base destructor.

是的,但您没有直接调用 ~Derived()。如果你要使用

Derived *p = new Derived();
delete p;

然后你就会得到错误。但是当您通过多态性间接访问 ~Derived() 时(例如通过调用 ~Base()),则访问说明符 private 不适用。

根据[class.access.virt#1]

The access rules (Clause [class.access]) for a virtual function are determined by its declaration and are not affected by the rules for a function that later overrides it. [ Example:

class B {
public:
  virtual int f();
};

class D : public B {
private:
  int f();
};

void f() {
  D d;
  B* pb = &d;
  D* pd = &d;

  pb->f();                      // OK: B​::​f() is public, D​::​f() is invoked
  pd->f();                      // error: D​::​f() is private
}

— end example ]

然后在 footnote 111 in [class.virtual]

Access control is not considered in determining overriding.

因为析构函数的调用顺序与构造函数的调用顺序相反,而虚拟析构函数将始终被调用。

private如果要调用虚函数,则无所谓。

正如我在这里指出的那样:

Why would a virtual function be private?

ISO C++ 1998 以后的标准明确指出:

§10.3 [...] Access control (clause 11) is not considered in determining overriding.


有点哲理的题外话:

更进一步,这就是 STL 为 iostreams 做的事情:Non-Virtual Interface 的定义,即所有 public 函数(析构函数除外)都是非虚函数,所有虚函数都是protectedprivate。 Public 函数调用虚拟保护或私有函数。这为整个层次结构提供了一个非常清晰的入口点。

您使用虚拟析构函数是因为您希望继承中的所有析构函数都被调用。这正是你得到的。 private 不适用,因为您没有显式调用析构函数。

如果您的析构函数不是虚拟的,您将只会被调用 Base::~Base()。当你有多态时,通常这不是你想要的。

您可以通过指向 B 的指针调用析构函数,因为它已经 public 在那里了。在这种情况下,它是用于确定访问权限的指针的静态类型。

[class.access.virt/1]

The access rules (Clause [class.access]) for a virtual function are determined by its declaration and are not affected by the rules for a function that later overrides it. [ Example:

class B {
public:
  virtual int f();
};

class D : public B {
private:
  int f();
};

void f() {
  D d;
  B* pb = &d;
  D* pd = &d;

  pb->f();                      // OK: B​::​f() is public, D​::​f() is invoked
  pd->f();                      // error: D​::​f() is private
}

— end example ]

这使虚函数与 Liskov Substitution Principle

保持一致

您通过基 class 指针删除派生的 class。在虚拟析构函数的帮助下,您可以在派生 class 处开始删除。因为 class 可以访问其私有成员,所以它调用私有析构函数。之后,基 class 调用 public 析构函数。

请注意,您调用 delete 而不是直接调用析构函数 Derived::~Derived()!