#include <iostream> using namespace std; struct A{ A(){cout<<"A::A"<<endl;} ~A(){cout<<"A::~A"<<endl;} }; struct B:public A{ B(){cout<<"B::B"<<endl;} ~B(){cout<<"B::~B"<<endl;} }; int main(){ A* a= new B; delete a; }
#include <iostream> using namespace std; struct A{ A(){cout<<"A::A"<<endl;} ~A(){cout<<"A::~A"<<endl;} }; struct B:public A{ B(){cout<<"B::B"<<endl;} ~B(){cout<<"B::~B"<<endl;} }; int main(){ A* a= new B; delete a; }
A::A B::B B::~B A::~A
A::A B::B A::~A B::~B
A::A B::B A::~A
A::A B::B B::~B
首先new运算符会调用B的构造函数,而在B的构造函数中又会调用A的构造函数(在所有的用户代码之前):
B() { // C++伪码 A::A(); // 在所有用户代码之前执行 cout<<"B::B"<<endl; }
因此会输出A::A,B::B
然后执行delete,由于A的析构函数不是virtual的,并且a是一个类型A的指针,所以只会调用A的析构函数而不是B的析构函数,结果输出A::~A。析构函数会以构造的相反顺序来执行:
// 若~A()加上了virtual ~A() { // C++伪码 B::~B(); // 在所有用户代码之前执行 cout<<"A::A"<<endl; }
上面构造函数和析构函数的调用都是由编译器自动为我们插入的代码,无需手动执行
如果~A()不加上virtual关键字,那么A的对象中是不存在vptr的(虚函数指针),也就没有多态性,那么delete a
会在编译期间被决议为对类型A的指针a进行操作,而不是在运行期间对指针a所指的真实对象进行操作,那么也就无法调用B的析构函数了
因此,若一个类继承自一个基类,而这个基类定义了析构函数,为了能够正确地调用派生类的析构函数,基类的析构函数最好加上virtual