【C++】为多态基类声明virtual析构函数
来自《Effective C++》条款07:为多态声明virtual析构函数
当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义——实际执行时通常发生的是对象的derived成分没被销毁,而其基类成分通常会被销毁,于是造成了一个诡异的“局部销毁”对象。
解决这个问题的做法很简单:给base class一个virtual析构函数。此后删除derived class对象就会如你想要的那般销毁整个对象。
当class不企图被当作base class,令其析构函数为virtual往往是个馊主意。许多人的心得是:只有当class内含至少一个virtual函数,才能为它声明virtual析构函数。
有时候令class带一个pure virtual析构函数,可能颇为遍历,pure virtual函数导致abstract classes——也就是不能被实体化的class。也就是说不能为那种类型创建对象。然而当你希望拥有抽象class,但手上没有任何pure function时就可以将把希望抽象成的那个class声明为一个pure virtual析构函数。下面是个例子:
class AWOV {
public:
virtual ~AWOV() == ;
};
这个class有一个pure virtual函数,所以它是一个抽象class,又由于它有个virtual析构函数,所以你不必担心析构函数的问题。然而这里有个窍门:你必须为这个pure virtual析构函数提供一份定义:
AWOV::~AWOV() {}
析构函数的运作方式是,最深层派生的那个class其析构函数最先被调用,然后是其每一个base class的析构函数被调用。编译器会在AWOV的derived classes的析构函数中创建一个对~AWOV的调用动作,所以你必须为这个函数提供一份定义。如果不这样做,连接器会发出抱怨。
请记住:
1. polymorphic(带多态性质的)base classes应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。
2. Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性(polymorphically),就不该声明virtual析构函数。
要分析为什么没有将基类的析构函数声明为virtual,只有基类部分会被销毁呢?这要首先明白C++中的名字查找与继承。理解函数调用的解析过程对于理解C++的继承至关重要,假设我们调用p->mem()(或者obj.mem()),则依次执行以下4个步骤:
1.首先确定p(或obj)的静态类型。因为我们调用的是一个成员,所以该类型必然是类类型。
2. 在p(或obj)的静态类型对应的类中查找mem。如果找不到,则依次在直接基类中不断查找直到到达继承链的顶端。如果找遍了该类及其基类仍然找不到,则编译器将报错。
3. 一旦找到了mem,就进行常规的类型检查以确认对于当前找到的mem,本次调用是合法的。
4. 假设调用合法,则编译器将根据调用的是否是虚函数而产生不同的代码:
——如果mem是虚函数且我们是通过引用或指针进行进行的调用,则编译器产生的代码将在运行时确定到底运行该虚函数的哪个版本,一句是对象的动态类型。
——如果mem不是虚函数或者我们是通过对象(而非引用或指针)进行的调用,则编译器将产生一个常规函数调用。
即
通过对象调用虚函数时,调用哪个类的函数取决于对象的类型。对象类型是基类时,就调用基类的函数;对象类型是子类时,就调用子类的函数。
使用指针访问非虚函数时,编译器根据指针本身的类型决定要调用哪个函数,而不是根据指针指向对象类型。
使用指针访问虚函数时,编译器根据指针所指对象的类型决定要调用哪个函数,而与指针本身类型无关。
可以看到如果我们将基类的指针指向动态分配的子类对象,而基类的析构函数没有声明为虚函数,那么按照名字查找就不会调用到派生类的析构函数。
参考资料:
1. 《Effective C++ 》第三版 电子工业出版社
2. 《C++ Primer》第五版 电子工业出版社 15.6
【C++】为多态基类声明virtual析构函数的更多相关文章
- 为多态基类声明virtual析构函数
一个函数的返回值为基类指针,而当指针指向一个派生类对象,接下来派生类对象被这个基类指针删除的时候,就出现了局部销毁的问题.因为C++指出,当派生类经由一个基类指针被删除,而该基类指针带着一个non-v ...
- 条款7:为多态基类声明virtual析构函数
C++明确指出:当派生类对象是由一个基类指针释放的,而基类中的析构函数不是虚函数,那么结果是未定义的.其实我们执行时其结果就是:只调用最上层基类的析构函数,派生类及其中间基类的析构函数得不到调用. # ...
- [Effective C++ --007]为多态基类声明virtual析构函数
引言: 我们都知道类的一个很明显的特性是多态,比如我们声明一个水果的基类: class Fruit { public: Fruit() {}; ~Fruit(){}; } 那么我们根据这个Fruit基 ...
- effective c++(07)之为多态基类声明virtual析构函数
class TimeKeeper { public: TimeKeeper() ; ~TimeKepper() ; ... } ; class AtomicClock:public TimeKeepe ...
- Effective C++_笔记_条款07_为多态基类声明virtual析构函数
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 这个规则只适用于polymorphic(带多态性质的)base ...
- NO.6: 为多态基类声明virtual析构函数
注意:polymorphic base class 应该具有虚析构函数,如果class带有任何virtual函数,也应具有虚析构函数 class不具备polymorphic属性则不应该声明virtua ...
- Effective C++(7) 为多态基类声明virtual析构函数 or Not
问题聚焦: 已经对一个对象执行了delete语句,还会发生内存泄漏吗? 先来看个demo: // 计时器类 class TimeKeeper { public: TimeKeeper(); ~Time ...
- Effective C++ -----条款07:为多态基类声明virtual析构函数
polymorphic(带多态性质的)base classes应该声明一个virtual析构函数.如果class带有任何virtual函数,它就应该拥有一个virtual析构函数. Classes的设 ...
- Effective C++学习笔记 条款07:为多态基类声明virtual析构函数
一.C++明确指出:当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未定义——实际执行时通常发生的是对象的 ...
随机推荐
- 20155302 2016-2017-2 《Java程序设计》 第1周学习总结
20155302 2016-2017-2 <Java程序设计> 第1周学习总结 教材学习内容总结 浏览全书提出问题 chapter1:怎么保证现在系统在用最高版本的JRE呢?在哪里查看及升 ...
- PostgreSQL的pg_stats学习
磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL统计信息索引页 回到顶级页面:PostgreSQL索引页 对于pg_stas,说明文档在这里: http://w ...
- day 3 模块
1.系统自带模块 xxx.py 文件 就是模块 ### 模块存放位置 In [1]: import os In [2]: os.__file__ Out[2]: '/usr/lib/python3. ...
- 【BZOJ1951】[SDOI2010]古代猪文
[BZOJ1951][SDOI2010]古代猪文 题面 bzoj 洛谷 题解 题目实际上是要求 $ G^{\sum d|n\;C_n^d}\;mod \; 999911659 $ 而这个奇怪的模数实际 ...
- 在Notepad++中为Python配置编译环境
方法1:按下F5 输入d:\Python25\python.exe "$(FULL_CURRENT_PATH)" 其中"d:\Python25\python.exe&qu ...
- nuget必备插件(待续)
DevLib.ExtensionMethods Extend Z.ExtensionMethods
- 在spring boot上基于maven使用redis——批量匹配并删除 (二)
一.背景 在搭建了项目之后,由于需要通过触发动作,并删除redis中多个key. 二.思路 在查询了jedis并没有类似的删除方法之后,事情就变得清晰起来.完成上述任务,分为两个步骤,第一,找到要删除 ...
- python3 - 元组、集合
元组(tuple) 有序集合,不可变 a(1,2,3) a[0]获取第一个值 集合(set)增删改 >>> b = set('abc') >>> bset(['a' ...
- 获取安卓app的appPackage和appActivity
1.需要配置好android的开发环境后,打开cmd命令窗口 2.在命令窗口中输入,adb logcat>D:/log.log,抓取日志 3.运行启动app 4.查看日志log 5.搜索日志的关 ...
- TW实习日记:第七天
今天早上,将项目的两个企业微信接口:登录和应用消息发送接口,做了最后的收尾工作,把目前我能解决的问题算是基本都解决了.早上还开了一个会,大意是组长封装了许多组件叫我们使用,在不断的使用中打磨组件的可用 ...