私有派生析构函数
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
不适用。
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 函数(析构函数除外)都是非虚函数,所有虚函数都是protected
或 private
。 Public 函数调用虚拟保护或私有函数。这为整个层次结构提供了一个非常清晰的入口点。
您使用虚拟析构函数是因为您希望继承中的所有析构函数都被调用。这正是你得到的。 private 不适用,因为您没有显式调用析构函数。
如果您的析构函数不是虚拟的,您将只会被调用 Base::~Base()
。当你有多态时,通常这不是你想要的。
您可以通过指向 B
的指针调用析构函数,因为它已经 public 在那里了。在这种情况下,它是用于确定访问权限的指针的静态类型。
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()!
当我尝试以多态方式删除派生对象时(即:基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
不适用。
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 函数(析构函数除外)都是非虚函数,所有虚函数都是protected
或 private
。 Public 函数调用虚拟保护或私有函数。这为整个层次结构提供了一个非常清晰的入口点。
您使用虚拟析构函数是因为您希望继承中的所有析构函数都被调用。这正是你得到的。 private 不适用,因为您没有显式调用析构函数。
如果您的析构函数不是虚拟的,您将只会被调用 Base::~Base()
。当你有多态时,通常这不是你想要的。
您可以通过指向 B
的指针调用析构函数,因为它已经 public 在那里了。在这种情况下,它是用于确定访问权限的指针的静态类型。
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()!