为什么要用虚析构函数
2015-07-15 17:57
169 查看
1 问题引出:为什么要使用虚析构函数?
运行结果:
注:
上面是传入父类指针并对其直接析构,如果不是对父类指针(或者引用)直接析构那一般不会出错,如 void main() {B b;A *p;p=&c;} 这时虽然使用了父类指针,但不是对父类指针析构,而是很明确直接析构b,所以用不用虚函数,也可以正确析构,
如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,使析构函数为虚一般是个坏主意。因为它会为类增加一个虚函数表,使得对象的体积翻倍,还有可能降低其可移植性。
所以基本的一条是:无故的声明虚析构函数和永远不去声明一样是错误的。实际上,很多人这样总结:当且仅当类里包含至少一个虚函数的时候才去声明虚析构函数。
2 那使用虚析构函数会有什么效果?
我们把析构函数前加上virtual关键字,来看一下效果。
运行结果如下:
在使用虚析构函数后,delete 明显变聪明了,知道应该先析构儿子,再去析构父亲了。但是在B类(儿子类)的析构函数刻意没有加 virtual 关键字,可见如同其他虚函数一样,只要父类加 virtual 就可以了。
引申:
虚析构函数,也是通过vftable来实现的,实际上当父类的析构函数声明为虚函数时,子类的析构函数也变成了虚函数(虽然两者函数名不同),即告诉编译器,我和我的派生类都需要动态(运行时)完成析构函数执行。
在编译器中我们可以看到:
析构函数的名字做了特殊处理,换成了‘vector deleting destructor’,用特定的标记,来标明析构函数。所以即使函数名不同,只要父类中使用 virtual ,也可以是所有子类的析构函数变成虚函数。
注:
为了方便对比,现给出包含多个虚函数的虚函数表,如下:
class A //父亲 { public: ~A() { cout << "调用了父亲的析构函数"<<endl; } }; class B : public A //儿子 { public: ~B() { cout << "调用了儿子的析构函数" << endl; } }; int main() { A *p; p = new B; delete p; system("pause"); return 0; }
运行结果:
我们知道在delete p; 中 delete 操作符,会调用对象的析构函数,但是这里传入的是父类对象指针,所以delete 此时并不知道应该调用哪个析构函数,保险起见则只调用父类的析构函数,也就是说如果没有使用虚析构函数,那么一般情况下只会去析构父类,而不会去析构子类。所以当delete p; 的时候,就会发生内存泄漏,也从而产生了异常。
注:
上面是传入父类指针并对其直接析构,如果不是对父类指针(或者引用)直接析构那一般不会出错,如 void main() {B b;A *p;p=&c;} 这时虽然使用了父类指针,但不是对父类指针析构,而是很明确直接析构b,所以用不用虚函数,也可以正确析构,
如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,使析构函数为虚一般是个坏主意。因为它会为类增加一个虚函数表,使得对象的体积翻倍,还有可能降低其可移植性。
所以基本的一条是:无故的声明虚析构函数和永远不去声明一样是错误的。实际上,很多人这样总结:当且仅当类里包含至少一个虚函数的时候才去声明虚析构函数。
2 那使用虚析构函数会有什么效果?
我们把析构函数前加上virtual关键字,来看一下效果。
class A //父亲 { public: virtual ~A() { cout << "调用了父亲的析构函数"<<endl; } }; class B : public A //儿子 { public: ~B() { cout << "调用了儿子的析构函数" << endl; } }; int main() { A *p; p = new B; delete p; system("pause"); return 0; }
运行结果如下:
在使用虚析构函数后,delete 明显变聪明了,知道应该先析构儿子,再去析构父亲了。但是在B类(儿子类)的析构函数刻意没有加 virtual 关键字,可见如同其他虚函数一样,只要父类加 virtual 就可以了。
引申:
虚析构函数,也是通过vftable来实现的,实际上当父类的析构函数声明为虚函数时,子类的析构函数也变成了虚函数(虽然两者函数名不同),即告诉编译器,我和我的派生类都需要动态(运行时)完成析构函数执行。
在编译器中我们可以看到:
析构函数的名字做了特殊处理,换成了‘vector deleting destructor’,用特定的标记,来标明析构函数。所以即使函数名不同,只要父类中使用 virtual ,也可以是所有子类的析构函数变成虚函数。
注:
为了方便对比,现给出包含多个虚函数的虚函数表,如下:
相关文章推荐
- 第2章 数字之魅——区间重合判断
- AWS S3使用小结
- vi(vim)键盘图及其基本命令
- 根文件系统制作 -- Kernel panic - not syncing
- ubuntu15.04安装jdk1.8.0_45和tomcat8
- NSString - 常用方法总结
- hadoop2.0报错“There appears to be a gap in the edit log”
- 分析ReentrantLock之lock
- 最简单的基于FFmpeg的推流器(以推送RTMP为例)
- 解决CPU/ABI No system images installed for this targe
- Python Windows下文件读写与二进制读写的区别
- SpringMVC——form标签的使用
- Java程序猿JavaScript学习笔记(14——扩大jQuery UI)
- Android IOS WebRTC 音视频开发总结(三四)-- windows.20150706
- css3制作时钟
- ps中的一些基本操作的作用
- SpringMVC——form标签的使用
- 关于QT利用setStyleSheet为主控件添加背景图片,而不影响其子控件的背景设置
- 快速傅里叶变化C++实现
- cas学习博客地址