1.当基类和派生类中都没有虚析构函数时
#include<iostream> using namespace std; class Base{ public: Base() { cout<<"调用Base类的构造函数Base()"<<endl; } ~Base() { cout<<"调用Base类的析构函数~Base()"<<endl; } virtual void print() { cout<<"调用函数virtual Base.print()"<<endl; } }; class subBase:public Base{ public: subBase() { cout<<"调用subBase类的构造函数subBase()"<<endl; } ~subBase() { cout<<"调用subBase类的析构函数~subBase()"<<endl; } virtual void print() { cout<<"调用函数virtual subBase.print()"<<endl; } }; void fun() { Base *b=new subBase; b->print(); delete b; } int main(int argc,char **argv) { fun(); return 0; }
从运行结果可以看出,当通过基类指针删除派生类对象时,派生类的析构函数根本没有调用。原因时派生类重写了基类析构函数,虽然派生类和基类的析构函数名字不同,但是编译器对析构函数做了特殊处理,在内部,派生类和基类的析构函数名字是一样的。所以当基类的析构函数为非虚函数时,就不能构成多态,基类的析构函数隐藏了派生类的析构函数,所以只能调用基类的析构函数。
2. 定义基类的虚析构函数
#include<iostream> using namespace std; class Base{ public: Base() { cout<<"调用Base类的构造函数Base()"<<endl; } virtual ~Base() { cout<<"调用Base类的析构函数~Base()"<<endl; } virtual void print() { cout<<"调用函数virtual Base.print()"<<endl; } }; class subBase:public Base{ public: subBase() { cout<<"调用subBase类的构造函数subBase()"<<endl; } ~subBase() { cout<<"调用subBase类的析构函数~subBase()"<<endl; } virtual void print() { cout<<"调用函数virtual subBase.print()"<<endl; } }; void fun() { Base *b=new subBase; b->print(); delete b; } int main(int argc,char **argv) { fun(); return 0; }
在上述代码中,基类的析构函数头部添加了关键字“virtual”,
从运行结果可以看出,定义了虚析构函数后,用一个基类的指针删除一个派生类的对象时,派生类的析构函数也被调用了。原因是当基类析构函数定义为虚函数后,删除对象时会直接调用派生类的析构函数,由于子类析构时会先调用父类的析构函数所以就把子类和继承的父类都析构了。
总结一下虚析构函数的作用:
(1)如果基类的析构函数不加virtual关键字
当基类的析构函数不声明成虚析构函数的时候,当派生类继承父类,基类的指针指向派生类时,delete掉基类的指针,只调动基类的析构函数,而不调动派生类的析构函数。
(2)如果基类的析构函数加virtual关键字
当基类的析构函数声明成虚析构函数的时候,当派生类继承基类,基类的指针指向派生类时,delete掉基类的指针,先调动派生类的析构函数,再调动基类的析构函数。
虚析构函数的原理分析
由于基类的析构函数为虚函数,所以派生类会在所有属性的前面形成虚表,而虚表内部存储的就是基类的虚函数。
当delete基类的指针时,由于派生类的析构函数与基类的析构函数构成多态,所以得先调动派生类的析构函数;之所以再调动基类的析构函数,是因为delete的机制所引起的,delete 基类指针所指的空间,要调用基类的析构函数。