我从赫伯·萨特(Herb Sutter)那里读到了这一点:
准则4:基类析构函数应该是公共的>虚拟的,或者是受保护的并且是非虚拟的。
但是,我正在努力理解后者的原因。 -具有非虚拟析构函数...我确实理解为什么需要对其进行保护。
这是我的代码:
-
become: true
hosts: all
remote_user: artur
tasks: ~
-
copy:
dest: /home/artur/grep_error.py
group: UnixUsers
mode: 420
owner: artur
src: /Users/artur/Desktop/sublime/projects/scripts/grep_error.py
name: "example copying file with owner and permissions"
通过将基本dtor设为非虚拟与虚拟,我可以获得什么?无论是虚拟还是非虚拟,我都能看到相同的效果。
#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;
}
答案 0 :(得分:2)
无论虚拟还是非虚拟,我都能看到相同的效果。
这是因为您通过指向派生类型的指针调用delete
。如果您改为将析构函数设为公开而非虚拟的,则执行
base* b = new derived();
delete b;
(最有可能)打印
base ctor
derived ctor
base dtor
done
(我不能保证它将打印出什么,因为如果析构函数不是虚拟的,则通过指向基类的指针删除派生类型的对象时,行为是不确定的)
在这种情况下,编译器既可以看到对new
的调用,也可以看到对delete
的调用,那么很可能会告诉您在这里您有未定义的行为,但是如果一个翻译单元只向您提供一个指针以基础为基础,而您以另一种方式调用delete
,则编译器无法知道。如果您想确定自己不会犯该错误,可以通过两种方法避免该问题。
首先是简单地使析构函数虚拟化;那么就不会有未定义的行为。但是当然,这具有较小的性能常量,因为销毁现在具有通过vtable的另一种间接访问级别。
因此,如果您从不打算将对象存储在基类的(智能)指针中,而仅通过引用使用多态性,那么您可能不想支付该额外费用。
因此,您需要另一种方法来防止某人实际上拥有派生类的对象时意外地在指向基类的指针上调用delete
:使得永远无法在对象上调用delete
基类的。这正是使析构函数protected
所具有的功能:派生类的析构函数仍可以根据需要调用基类的析构函数,但指向基类的指针的用户不再可以delete
该指针,因为它不可访问。