C++ 拥有“受保护的非虚拟析构函数”与“受保护的虚拟析构函数”相比有什么好处?

C++ 拥有“受保护的非虚拟析构函数”与“受保护的虚拟析构函数”相比有什么好处?,c++,polymorphism,C++,Polymorphism,从赫伯·萨特那里,我可以看出这一点: 准则#4:基类析构函数应该是公共的>虚拟的,或者是受保护的非虚拟的 然而,我正在努力理解后者背后的原因有一个非虚拟的析构函数。。。我确实理解为什么需要保护它 这是我的密码: #include <iostream> using namespace std; class base { public: base () {cout << "base ctor" << endl;} virtual void foo

从赫伯·萨特那里,我可以看出这一点:

准则#4:基类析构函数应该是公共的>虚拟的,或者是受保护的非虚拟的

然而,我正在努力理解后者背后的原因有一个非虚拟的析构函数。。。我确实理解为什么需要保护它

这是我的密码:

#include <iostream>
using namespace std;

class base {
public:
    base () {cout << "base ctor" << endl;}
    virtual void foo () = 0;
protected:
    ~base() {cout << "base dtor" << endl;}
};
class derived : public base {
public:
        derived () {cout << "derived ctor" << endl;}
        virtual void foo () override {cout << "derived foo" << endl;}
        virtual ~derived(){cout << "derived dtor" << endl;}
};

int main(){
    derived* b = new derived();
        delete b;
    cout <<"done"<<endl;
}
无论是虚拟的还是非虚拟的,我都能看到同样的效果

base ctor
derived ctor
derived dtor
base dtor
done
这是因为您通过指向派生类型的指针调用
delete
。如果您将析构函数设置为公共的,但不是虚拟的,那么

base* b = new derived();
delete b;
它(很可能)会打印出来

(我不能保证它将打印什么,因为如果析构函数不是虚拟的,则通过指向基类的指针删除派生类型的对象时,行为是未定义的)

在这种情况下,编译器可以同时看到对
new
delete
的调用,它很可能会告诉您这里有未定义的行为,但是如果一个翻译单元只是给您一个指向base的指针,而您在另一个单元中调用
delete
,编译器将无法知道。如果你想确保自己不会犯这样的错误,有两种方法可以避免这个问题

第一种方法是简单地将析构函数虚拟化;这样就不会有未定义的行为。但当然,这有一个小的性能常数,因为破坏现在通过vtable有一个额外的间接级别

因此,如果您从未打算将对象存储在指向基类的(智能)指针中,并且只通过引用使用多态性,那么您可能不想支付额外的成本


因此,当您实际拥有派生类的对象时,您需要另一种方法来防止有人在指向基类的指针上意外调用
delete
:使基类的对象永远无法调用
delete
。这正是使析构函数
受保护
所能提供的:派生类的析构函数仍然可以根据需要调用基类析构函数,但是指向基类的指针的用户不能再通过该指针意外地
删除
,因为它不可访问。

谢谢,但这并不能真正回答我的问题。通过保护基dtor,已经给出了一个无法删除指向基dtor的指针的条件。我的问题是为什么herb建议将基本dtor设置为非虚拟与虚拟?@HossAja正如我在回答中指出的那样,与非虚拟析构函数相比,虚拟析构函数的性能开销较大,因此,如果不需要,您可能不想支付该价格。更多信息:
base ctor
derived ctor
base dtor
done