一、引入
如果通过一个基类指针申请一个派生类对象,那么在通过这个指针释放对象的时候,要求基类的虚函数是virtual的 。
二、虚析构函数
使用方法和规则与虚函数一样
格式要求:
虚析构函数要求基类与派生类中的名称不一致
只要基类的析构函数是虚函数,就能确保我们在释放指针时准确的运行哪个版本(基类or派生类)的析构函数
如果基类指针指向于自己,那么delete的时候执行的就是自己的析构函数
如果基类指针指向于派生类对象,那么delete的时候执行的就是派生类的析构函数(这个就是多态的性质,与执行虚函数的原理一样)
如果基类的析构函数不是虚函数,则delete一个指向派生类对象的基类指针将产生未定义的行为
与虚函数一样,如果基类的析构函数为virtual,那么派生类的析构函数也都是virtual的(即使是编译器默认合成的也是virtual的)
三、虚析构函数的其它注意事项
①前面我们介绍过如果一个类需要析构函数,那么它同样需要拷贝和赋值操作。但是基类的虚析构函数并不遵循这个规则:一个基类总是需要析构函数,而且它能将析构函数设定为虚函数,此时,该析构函数为了成为虚函数而令内容为空,我们显然无法由此推断该基类还释放需要复制运算符或拷贝构造函数
②虚析构函数将阻止合成移动操作:基类需要一个虚析构函数这一事实还会对基类和派生类的定义产生另外一个间接的影响:如果一个类定义了析构函数,即使它通过default的形式使用了合成的版本,编译器也不会为这个类合成一定操作(见合成移动操作(待续))
四、演示案例
#include <string>#include <iostream>using namespace std;class A {private: int a;public: A() { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; }};class B :public A {public: int b; B(){ cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }}; int main() { A* pa = new B(); delete pa; int a = 0;}
输出:
A()
B()
~A()
如果我们把A的析构函数声明成虚拟函数
#include <string>
#include <iostream>
using namespace std;
class A {
private:
int a;
public:
A() {
cout << "A()" << endl;
}
virtual ~A() {
cout << "~A()" << endl;
}
};
class B :public A {
public:
int b;
B(){
cout << "B()" << endl;
}
~B() {
cout << "~B()" << endl;
}
};
int main() {
A* pa = new B();
delete pa;
int a = 0;
}
这样就会得到我们想要的结果:
A()
B()
~B()
~A()