C++明确指出:当派生类对象是由一个基类指针释放的,而基类中的析构函数不是虚函数,那么结果是未定义的。其实我们执行时其结果就是:只调用最上层基类的析构函数,派生类及其中间基类的析构函数得不到调用。
1 #include2 3 using namespace std; 4 5 class TimeKeeper 6 { 7 public: 8 TimeKeeper(); 9 ~TimeKeeper();10 };11 TimeKeeper::TimeKeeper()12 {13 cout << "Construct TimeKeeper" << endl;14 }15 TimeKeeper::~TimeKeeper()16 {17 cout << "Destruct TimeKeeper" << endl;18 }19 20 class WristWatch : public TimeKeeper21 {22 public:23 WristWatch();24 ~WristWatch();25 };26 WristWatch::WristWatch()27 {28 cout << "Construct WristWatch" << endl;29 }30 WristWatch::~WristWatch()31 {32 cout << "Destruct WristWatch" << endl;33 }34 35 int main()36 {37 TimeKeeper* pt = new WristWatch;38 delete pt; // 仅调用TimeKeeper::~TimeKeeper39 40 return 0;41 }
现在我们将基类的析构函数变为虚析构,代码只改动一行,在~TimeKeeper()前面加上virtual,那么用基类指针释放派生类对象时,就会先调用WristWatch::~WristWatch,然后调用TimeKeeper::~TimeKeeper。
注意:
1> 如果在定义一个类时可以确保该类不会作为多态的基类,那么不要为其定义虚析构函数。因为虚函数的实现机制会增大对象的空间(必须保存一个指向vtable的vptr指针,会占用32bit或者64bit的存储空间)。因此,经验是:只有当一个class中至少含有一个virtual函数,才为其定义virtual析构函数。
2> 不要从non-virtual析构函数的类型继承。
有时候让一个类带有pure virtual析构函数更便利一些:
1 class Base2 {3 public:4 virtual ~Base() = 0; // pure virtual destructor5 };6 Base::~Base() // definition7 {8 9 }
主要有两方面的好处:
1> 你想拥有一个抽象类(接口),但还没找到任何有用的virtual函数可供使用
2> 同时解决了多态的析构调用问题。
但此时你必须注意:必须为这个pure virtual析构函数提供一个实现。因为在析构过程中,编译器会在派生类的析构函数中调用基类的析构函数,如果没定义,则必然发生错误。
总结:
- 多态基类必须声明一个virtual析构函数。
- 如果一个类中至少有一个virtual函数,说明该类的目的是作为多态基类存在,它就必须拥有一个virtual析构函数。
- 如果设计一个类的目的不是作为基类使用,或者是基类但不具有多态性,那么不要声明virtual析构函数。