【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析构函数,其结果未定义——实际执行时通常发生的是对象的 ...
随机推荐
- 20155217 2016-2017-2 《Java程序设计》第2周学习总结
20155217 2016-2017-2 <Java程序设计>第2周学习总结 教材学习内容总结 Java中的基本类型主要可分为整数.字节.浮点数.字符与布尔. 整数可分为short整数(占 ...
- 20155319 2016-2017-2 《Java程序设计》第四周学习总结
20155319 2016-2017-2 <Java程序设计>第四周学习总结 教材学习内容总结 ==继承== 6.1.1 继承共同行为 定义:继承基本上就是避免多个类间重复定义共同行为. ...
- 20155323 2016-2017-2 《Java程序设计》第2周学习总结
20155323 2016-2017-2 <Java程序设计>第2周学习总结 教材学习内容总结 对象:对象是类的一个实例,有状态和行为. 类:类是一个模板,它描述一类对象的行为和状态. 第 ...
- 20155333 2016-2017-2《Java程序设计》课程总结
20155333 2016-2017-2<Java程序设计>课程总结 (按顺序)每周作业链接汇总 预备作业1:你期望的师生关系是什么? 预备作业2:体会做中学(Learing By Doi ...
- 20145234黄斐《java程序设计基础》第一周
教材学习内容总结 1.java是SUN公司推出的面相网络的编程语言,当时取名Oak,后改名Java. 2.Java三大平台分别为:Java SE.Java EE与Java ME. 3.Java不仅是程 ...
- PostgreSQL Streaming Replication的FATAL ERROR
磨砺技术珠矶,践行数据之道,追求卓越价值回到上一级页面: PostgreSQL集群方案相关索引页 回到顶级页面:PostgreSQL索引页[作者 高健@博客园 luckyjackgao@gm ...
- Swift3.0字符串大小写转化
Swift3.0语言教程字符串大小写转化,在字符串中,字符串的格式是很重要的,例如首字母大写,全部大写以及全部小写等.当字符串中字符很多时,通过人为一个一个的转换是很费时的.在NSString中提供了 ...
- Pomelo.AspNetCore.TimedJob 允许依赖注入
public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStar ...
- 【转】CentOS Linux解决Device eth0 does not seem to be present(linux)
原文来自:http://www.linuxidc.com/Linux/2012-12/76248.htm 在VMware里克隆出来的CentOS Linux.. ifconfig...没有看到eth0 ...
- Dubbo使用心得2