来自《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析构函数的更多相关文章

  1. 为多态基类声明virtual析构函数

    一个函数的返回值为基类指针,而当指针指向一个派生类对象,接下来派生类对象被这个基类指针删除的时候,就出现了局部销毁的问题.因为C++指出,当派生类经由一个基类指针被删除,而该基类指针带着一个non-v ...

  2. 条款7:为多态基类声明virtual析构函数

    C++明确指出:当派生类对象是由一个基类指针释放的,而基类中的析构函数不是虚函数,那么结果是未定义的.其实我们执行时其结果就是:只调用最上层基类的析构函数,派生类及其中间基类的析构函数得不到调用. # ...

  3. [Effective C++ --007]为多态基类声明virtual析构函数

    引言: 我们都知道类的一个很明显的特性是多态,比如我们声明一个水果的基类: class Fruit { public: Fruit() {}; ~Fruit(){}; } 那么我们根据这个Fruit基 ...

  4. effective c++(07)之为多态基类声明virtual析构函数

    class TimeKeeper { public: TimeKeeper() ; ~TimeKepper() ; ... } ; class AtomicClock:public TimeKeepe ...

  5. Effective C++_笔记_条款07_为多态基类声明virtual析构函数

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 这个规则只适用于polymorphic(带多态性质的)base ...

  6. NO.6: 为多态基类声明virtual析构函数

    注意:polymorphic base class 应该具有虚析构函数,如果class带有任何virtual函数,也应具有虚析构函数 class不具备polymorphic属性则不应该声明virtua ...

  7. Effective C++(7) 为多态基类声明virtual析构函数 or Not

    问题聚焦: 已经对一个对象执行了delete语句,还会发生内存泄漏吗? 先来看个demo: // 计时器类 class TimeKeeper { public: TimeKeeper(); ~Time ...

  8. Effective C++ -----条款07:为多态基类声明virtual析构函数

    polymorphic(带多态性质的)base classes应该声明一个virtual析构函数.如果class带有任何virtual函数,它就应该拥有一个virtual析构函数. Classes的设 ...

  9. Effective C++学习笔记 条款07:为多态基类声明virtual析构函数

    一.C++明确指出:当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未定义——实际执行时通常发生的是对象的 ...

随机推荐

  1. 20155302 2016-2017-2 《Java程序设计》 第1周学习总结

    20155302 2016-2017-2 <Java程序设计> 第1周学习总结 教材学习内容总结 浏览全书提出问题 chapter1:怎么保证现在系统在用最高版本的JRE呢?在哪里查看及升 ...

  2. PostgreSQL的pg_stats学习

    磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL统计信息索引页     回到顶级页面:PostgreSQL索引页 对于pg_stas,说明文档在这里: http://w ...

  3. day 3 模块

    1.系统自带模块  xxx.py 文件 就是模块 ### 模块存放位置 In [1]: import os In [2]: os.__file__ Out[2]: '/usr/lib/python3. ...

  4. 【BZOJ1951】[SDOI2010]古代猪文

    [BZOJ1951][SDOI2010]古代猪文 题面 bzoj 洛谷 题解 题目实际上是要求 $ G^{\sum d|n\;C_n^d}\;mod \; 999911659 $ 而这个奇怪的模数实际 ...

  5. 在Notepad++中为Python配置编译环境

    方法1:按下F5 输入d:\Python25\python.exe "$(FULL_CURRENT_PATH)" 其中"d:\Python25\python.exe&qu ...

  6. nuget必备插件(待续)

    DevLib.ExtensionMethods Extend Z.ExtensionMethods

  7. 在spring boot上基于maven使用redis——批量匹配并删除 (二)

    一.背景 在搭建了项目之后,由于需要通过触发动作,并删除redis中多个key. 二.思路 在查询了jedis并没有类似的删除方法之后,事情就变得清晰起来.完成上述任务,分为两个步骤,第一,找到要删除 ...

  8. python3 - 元组、集合

    元组(tuple) 有序集合,不可变 a(1,2,3) a[0]获取第一个值 集合(set)增删改 >>> b = set('abc') >>> bset(['a' ...

  9. 获取安卓app的appPackage和appActivity

    1.需要配置好android的开发环境后,打开cmd命令窗口 2.在命令窗口中输入,adb logcat>D:/log.log,抓取日志 3.运行启动app 4.查看日志log 5.搜索日志的关 ...

  10. TW实习日记:第七天

    今天早上,将项目的两个企业微信接口:登录和应用消息发送接口,做了最后的收尾工作,把目前我能解决的问题算是基本都解决了.早上还开了一个会,大意是组长封装了许多组件叫我们使用,在不断的使用中打磨组件的可用 ...