以前,知道了虚函数表的低效性之后,一直尽量避免使用之。所以,在最近的工程中,所有的析构函数都不是虚函数。
今天趁着还书的机会到图书馆,还书之后在 TP 分类下闲逛,偶然读到一本游戏编程书,里面说建议将存在派生的类的析构函数都设置为 virtual。例如 ParentClass 和 ChildClass(派生自 ParentClass),如果 ParentClass 的 ~ParentClass() 不是 virtual 的话,以下代码会产生潜在的问题:

1 ParentClass *pClass = new ChildClass();
2 delete pClass;

有什么问题呢?~ChildClass() 此时不会被调用。
于是想起来,赶快回来改代码!
我觉得其实析构函数也遵循 virtual 修饰的规则嘛。之前的例子,delete 的时候其实调用的是 ~ParentClass(),因为该函数不是虚函数;而如果是 virtual ~ParentClass() 的话,~ParentClass() 实际上是在虚函数表里的,因此会调用覆盖(override)之的 ~ChildClass()。
实际情况是否是这样的呢?我写了一个小小的示例,展示析构函数修饰符的影响。其中,后缀“v”表示析构函数是虚函数。

  1 #include <stdio.h>
  2
  3 class P
  4 {
  5 public:
  6     P() {}
  7     ~P()
  8     {
  9         printf("P destruction\n");
 10     }
 11 };
 12
 13 class Pv
 14 {
 15 public:
 16     Pv() {}
 17     virtual ~Pv()
 18     {
 19         printf("Pv destruction\n");
 20     }
 21 };
 22
 23 class CP
 24     : public P
 25 {
 26 public:
 27     CP() {}
 28     ~CP()
 29     {
 30         printf("CP destruction\n");
 31     }
 32 };
 33
 34 class CPv
 35     : public Pv
 36 {
 37 public:
 38     CPv() {}
 39     ~CPv()
 40     {
 41         printf("CPv destruction\n");
 42     }
 43 };
 44
 45 class CvP
 46     : public P
 47 {
 48 public:
 49     CvP() {}
 50     virtual ~CvP()
 51     {
 52         printf("CvP destruction\n");
 53     }
 54 };
 55
 56 class CvPv
 57     : public Pv
 58 {
 59 public:
 60     CvPv() {}
 61     virtual ~CvPv()
 62     {
 63         printf("CvPv destruction\n");
 64     }
 65 };
 66
 67 int main(int argc, char *argv[])
 68 {
 69     P *p = new P();
 70     Pv *pv = new Pv();
 71     P *pc = new CP();
 72     //P *pcv = new CvP(); // 析构时崩溃
 73     Pv *pvc = new CPv();
 74     Pv *pvcv = new CvPv();
 75     CP *cp = new CP();
 76     CPv *cpv = new CPv();
 77     CvP *cvp = new CvP();
 78     CvPv *cvpv = new CvPv();
 79
 80     printf("-----------------------------\n");
 81     delete p;
 82     printf("-----------------------------\n");
 83     delete pv;
 84     printf("-----------------------------\n");
 85     delete pc;
 86     printf("-----------------------------\n");
 87     //delete pcv; // 父类析构调用没问题,然后崩溃
 88     printf("-----------------------------\n");
 89     delete pvc;
 90     printf("-----------------------------\n");
 91     delete pvcv;
 92     printf("-----------------------------\n");
 93     delete cp;
 94     printf("-----------------------------\n");
 95     delete cpv;
 96     printf("-----------------------------\n");
 97     delete cvp;
 98     printf("-----------------------------\n");
 99     delete cvpv;
100     printf("-----------------------------\n");
101
102     return 0;
103 }

其中删除静态类型为 P * 动态类型为 CvP * 的 pcv 时会崩溃。
其余结果如下:

-----------------------------
P destruction
-----------------------------
Pv destruction
-----------------------------
P destruction
-----------------------------
-----------------------------
CPv destruction
Pv destruction
-----------------------------
CvPv destruction
Pv destruction
-----------------------------
CP destruction
P destruction
-----------------------------
CPv destruction
Pv destruction
-----------------------------
CvP destruction
P destruction
-----------------------------
CvPv destruction
Pv destruction
-----------------------------

可见,我的想法不是完全正确的。

总结一下,在10种使用方式中,有两种是不好的:

  1. 父类析构函数非虚函数,子类析构函数是虚函数,使用父类作为静态类型的析构(崩溃);
  2. 父类析构函数非虚函数,子类析构函数非虚函数,使用父类作为静态类型的析构(跳过了子类的析构函数)。

其余情况下,只要父类的析构函数是虚函数,就不需要关心指针的静态类型;统一指针的静态类型和动态类型(显式让运行时调用子类的析构函数)也可以避免意外。

转载于:https://www.cnblogs.com/GridScience/p/3716649.html

virtual 修饰符与继承对析构函数的影响(C++)相关推荐

  1. java修饰符继承_Java修饰符和继承

    您可能感兴趣的话题: Java 核心提示:private 访问局限在同一个类内,并且不可以被继承. java类的成员变量通常有以下几种访问修饰符: public.private.protected或者 ...

  2. c# 访问修饰符的访问权限

    1. 访问修饰符. 指定声明的类型和类型成员的可访问性. (1) public:是类型和类型成员的访问修饰符.公共访问是允许的最高访问级别.对访问公共成员没有限制. (2) private:是一个成员 ...

  3. python修饰符用法_c#教程之C#语言中的修饰符汇总

    https://www.xin3721.com/eschool/python.html 修饰符是用于限定类型以及类型成员的申明的一种符号. 下面主要从C#中的访问修饰符,作用于类和结构的修饰符,用在方 ...

  4. 简述c#之sealed 修饰符

    sealed 修饰符表示密封 用于类时,表示该类不能再被继承,不能和 abstract 同时使用,因为这两个修饰符在含义上互相排斥 用于方法和属性时,表示该方法或属性不能再被重写,必须和 overri ...

  5. C# 访问修饰符和声明修饰符

    一.访问修饰符的基本说明 1.public: 公有的,是类和类成员的访问修饰符,访问不受限制 2.private: 私有的,是一个成员访问修饰符,不能修饰类,只有在声明它的类和结构内部可以访问 3.i ...

  6. Java修饰符的访问权限

    参考资料: http://blog.csdn.net/yan8024/article/details/6426451 http://liqita.iteye.com/blog/1216082 priv ...

  7. python修饰符作用_python函数修饰符@的使用

    python函数修饰符@的作用是为现有函数增加额外的功能,常用于插入日志.性能测试.事务处理等等. 创建函数修饰符的规则: (1)修饰符是一个函数 (2)修饰符取被修饰函数为参数 (3)修饰符返回一个 ...

  8. 第九天2017/04/18(2、类的继承、面试题:继承访问修饰符、组合、static、构造、多态)

    继承:可以使用原来的代码,代码复用 多态:代码复用.接口复用,用基类的指针"根据对象"调用"指定对象的函数". 1.继承.访问修饰符//C++类成员的3种访问级 ...

  9. 访问修饰符,封装,继承

    一.封装: 封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类).被封装的对象通常被称为抽象数据类型. 封装的意义: 封装的意义在于保护或者防止代码(数据)被 ...

最新文章

  1. 错误 C2664 “int WideCharToMultiByte......”: 无法将参数 3 从“CString”转换为“LPCWCH” 的问题解决
  2. 百度超级搜索技巧集锦
  3. WSS 扩展文件夹的属性--如何给文件夹添加扩展字段
  4. 神奇的幻方(洛谷P2615题题解,Java语言描述)
  5. php int类型思索
  6. 95-30-025-java.util-AbstractMap
  7. RabbitMQ (一)第一个hello world
  8. 医疗数据分析——过高费用的异常检测
  9. android减少动态效果,【技巧】手机运行变慢?试试这些办法!
  10. CKEditor 5 在线编辑 PDF
  11. 微信客服系统开发SDK使用教程-给好友发消息任务
  12. gin-binding参数效验
  13. matlab神经网络工具箱的使用
  14. 比尔·盖茨买百万亩农地成美“头号地主”,图扑数字孪生农场
  15. linux卡住重启_linux df -h 命令卡住 解决方法
  16. 操作无法完成因为文件已在syayem中打开怎么处理删除文件。
  17. Web 实现前后端分离,前后端解耦
  18. 二阶龙格库塔公式推导_连续系统数值仿真方法——龙格库塔法
  19. SQL Compare教程:工作示例——比较和部署两个数据库(下)
  20. MATLAB GUI设计 多个选项卡/子页面

热门文章

  1. linux怎么看系统盘,Linux系统怎么查看电脑的磁盘空间?
  2. 树莓派桌面没有时间_树莓派日期时间不准的修正方法
  3. 大一考二级c语言,大专学生计算机二级是大一考还是大二考
  4. 客户端分析php代码,分享:一个简单的全网解析客户端代码。
  5. linux安装mysql.rpm软件包_Linux环境安装MySQL数据库(RPM格式的软件包)
  6. java迷宫类编程题_第十届蓝桥杯省赛java类B组 试题 E:迷宫 (动态规划之回溯法)...
  7. unity运行环境_LG电子与Unity合作仿真软件 加速研发更安全的自动驾驶汽车系统...
  8. php 类别名,关于php:从类别ID laravel获取类别名称
  9. DataFrame列转json以及json转DataFrame列
  10. android获取有线、wifi、3G(4G)的IP