1

当我试图以多态方式删除派生对象(即:基类具有公共虚拟析构函数)时,为什么仍在调用派生类私有析构函数?为什么私有范围解析在这里不起作用。

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.
4

5 回答 5

3

是的,但你没有~Derived()直接打电话。如果你要使用

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

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

根据[class.access.virt#1]

虚函数的访问规则(子句 [class.access])由其声明决定,不受稍后覆盖它的函数规则的影响。[ 例子:

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
}

—结束示例]

再次在[class.virtual] 的脚注 111 中

在确定覆盖时不考虑访问控制。

于 2017-05-10T11:06:34.217 回答
3

因为析构函数的调用顺序与构造函数相反,所以总是会调用虚拟析构函数。

private如果要调用虚函数,则无关紧要。

正如我在这里指出的:

为什么虚函数是私有的?

ISO C++ 1998 标准及以后明确规定:

§10.3 [...] 在确定覆盖时不考虑访问控制(第 11 条)。


有点哲学的题外话:

更进一步,这就是 STL 所做的iostreams非虚拟接口的定义,即所有公共函数(除了析构函数)都是非虚拟的,所有虚拟函数都是protectedor private。公共函数称为虚拟保护或私有函数。这为整个层次结构提供了一个非常清晰的入口点。

于 2017-05-10T11:11:43.223 回答
0

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

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

于 2017-05-10T11:12:35.397 回答
0

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

[class.access.virt/1]

虚函数的访问规则(子句 [class.access])由其声明决定,不受稍后覆盖它的函数规则的影响。[ 例子:

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
}

—结束示例]

这使虚函数符合Liskov 替换原则

于 2017-05-10T11:20:08.977 回答
0

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

请注意,您调用delete而不是直接调用 destructor Derived::~Derived() !

于 2017-05-10T12:25:52.373 回答