假设有基类Base,基类的公有派生类Derived,全局函数fun; fun函数和主函数的代码为:
void fun(Base* b) {
delete b;
}
int main() {
Base *b = new Derived();
fun(b);
return 0;
}
虽然我们定义b为指向基类对象的指针,但是这里用b指向动态分配的派生类对象也是可行的,接着就是执行fun(b)。
大家都知道,如果基类的析构函数不是虚函数,fun(b)的结果就是只调用基类的析构函数,不调用派生类的析构函数的错误情况,正确的方法是将基类和派生类的析构函数都写成虚函数。
但是我后来又想了一下,如果只将基类的析构函数定义成虚函数,派生类的析构函数不定义成虚函数,那么结果会怎样呢?运行的结果和上述正确方法的运行结果是一样的。
然后果断总结一波,派生类的析构函数是否定义成虚函数,对结果没有影响,可以这样理解:
因为b定义为的是指向基类Base的指针,所以编译器在“delete b”的编译阶段,只会去判断“是”或“否”调用基类的析构函数,而不会去判断“是”或“否”调用派生类的析构函数。
当基类的析构函数不是虚函数,编译器就“会”在编译阶段判断调用基类的析构函数,不会等到运行阶段再判断调用谁的析构函数;
当基类的析构函数是虚函数,编译器就“不会”在编译阶段判断调用谁的析构函数,而会等到运行阶段再根据指针实际指向的对象去调用基类还是派生类的析构函数。
所以,没有virtual关键字就像死刑立即执行,有virtual关键字更像是缓期执行,让你有更充足的准备(指针实际指向的类对象)去判断到底该调用谁?
最后贴上完整的代码:
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base();
};
Base::~Base() {
cout << "Base destructor" << endl;
}
class Derived : public Base {
public:
Derived();
~Derived();
//virtual ~Derived();//派生类的析构函数是否定义成虚函数,对结果没有影响
private:
int *p;
};
Derived::Derived() {
p = new int(0);
}
Derived::~Derived() {
cout << "Derived destructor" << endl;
delete p;
}
void fun(Base* b) {
delete b;
}
int main() {
Base *b = new Derived();
fun(b);
return 0;
}