virtual 修饰符与继承对析构函数的影响(C++)
以前,知道了虚函数表的低效性之后,一直尽量避免使用之。所以,在最近的工程中,所有的析构函数都不是虚函数。
今天趁着还书的机会到图书馆,还书之后在 TP 分类下闲逛,偶然读到一本游戏编程书,里面说建议将存在派生的类的析构函数都设置为 virtual。例如 ParentClass 和 ChildClass(派生自 ParentClass),如果 ParentClass 的 ~ParentClass() 不是 virtual 的话,以下代码会产生潜在的问题:
ParentClass *pClass = new ChildClass();
delete pClass;
有什么问题呢?~ChildClass() 此时不会被调用。
于是想起来,赶快回来改代码!
我觉得其实析构函数也遵循 virtual 修饰的规则嘛。之前的例子,delete 的时候其实调用的是 ~ParentClass(),因为该函数不是虚函数;而如果是 virtual ~ParentClass() 的话,~ParentClass() 实际上是在虚函数表里的,因此会调用覆盖(override)之的 ~ChildClass()。
实际情况是否是这样的呢?我写了一个小小的示例,展示析构函数修饰符的影响。其中,后缀“v”表示析构函数是虚函数。
#include <stdio.h> class P
{
public:
P() {}
~P()
{
printf("P destruction\n");
}
}; class Pv
{
public:
Pv() {}
virtual ~Pv()
{
printf("Pv destruction\n");
}
}; class CP
: public P
{
public:
CP() {}
~CP()
{
printf("CP destruction\n");
}
}; class CPv
: public Pv
{
public:
CPv() {}
~CPv()
{
printf("CPv destruction\n");
}
}; class CvP
: public P
{
public:
CvP() {}
virtual ~CvP()
{
printf("CvP destruction\n");
}
}; class CvPv
: public Pv
{
public:
CvPv() {}
virtual ~CvPv()
{
printf("CvPv destruction\n");
}
}; int main(int argc, char *argv[])
{
P *p = new P();
Pv *pv = new Pv();
P *pc = new CP();
//P *pcv = new CvP(); // 析构时崩溃
Pv *pvc = new CPv();
Pv *pvcv = new CvPv();
CP *cp = new CP();
CPv *cpv = new CPv();
CvP *cvp = new CvP();
CvPv *cvpv = new CvPv(); printf("-----------------------------\n");
delete p;
printf("-----------------------------\n");
delete pv;
printf("-----------------------------\n");
delete pc;
printf("-----------------------------\n");
//delete pcv; // 父类析构调用没问题,然后崩溃
printf("-----------------------------\n");
delete pvc;
printf("-----------------------------\n");
delete pvcv;
printf("-----------------------------\n");
delete cp;
printf("-----------------------------\n");
delete cpv;
printf("-----------------------------\n");
delete cvp;
printf("-----------------------------\n");
delete cvpv;
printf("-----------------------------\n"); return ;
}
其中删除静态类型为 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种使用方式中,有两种是不好的:
- 父类析构函数非虚函数,子类析构函数是虚函数,使用父类作为静态类型的析构(崩溃);
- 父类析构函数非虚函数,子类析构函数非虚函数,使用父类作为静态类型的析构(跳过了子类的析构函数)。
其余情况下,只要父类的析构函数是虚函数,就不需要关心指针的静态类型;统一指针的静态类型和动态类型(显式让运行时调用子类的析构函数)也可以避免意外。
virtual 修饰符与继承对析构函数的影响(C++)的更多相关文章
- virtual 修饰符 C# .NET
virtual 关键字用于修饰方法.属性.索引器或事件声明,并且允许在派生类中重写这些对象. 例如,此方法可被任何继承它的类重写. (C#参考) public virtual double Area( ...
- virtual修饰符
virtual(C# 参考) virtual 关键字用于修饰方法.属性.索引器或事件声明,并使它们可以在派生类中被重写. 例如,此方法可被任何继承它的类重写. public virtual doubl ...
- 概述C# virtual修饰符
摘要:C#是继C++和Java语言后的又一面向对象的语言,在语法结构,C#有很多地方和C++及Java相似,但是又不同于它们,其中一些关键特别需要引起我们的注意. C# virtual修饰符用于修改方 ...
- C# abstract,virtual 修饰符
abstract(抽象):该abstract修饰符指示要修改的东西有缺失或不完整的实现.abstract修饰符可以与类,方法,属性,索引器和事件一起使用.abstract在类声明中使用修饰符来指示类仅 ...
- 面向对象_访问修饰符_构造与析构函数_this指针
1:面向对象 以codeblocks举例,在一个工程里面: File-->new -->Class可以建一个类,可以设置类的参数,是否有set get方法,有无构造函数等设置,.h文件主要 ...
- C#继承机制 继承与访问修饰符
继承与访问修饰符 访问修饰符是一些关键字,用于指定声明的成员或类型的可访问性.类的继承中有四个访问修饰符: public protected internal private.使用这些访问修饰符可指定 ...
- c# 访问修饰符的访问权限
1. 访问修饰符. 指定声明的类型和类型成员的可访问性. (1) public:是类型和类型成员的访问修饰符.公共访问是允许的最高访问级别.对访问公共成员没有限制. (2) private:是一个成员 ...
- C#修饰符讲解大全
1.修饰符是什么? 修饰符是用于限定类型以及类型成员的声明的一种符号.[百度百科] 2.修饰符分类 13种修饰符,按功能可分为三类:访问修饰符,类修饰符和成员修饰符.[百度百科] 作 用:限定类型以及 ...
- override 修饰符
override(C# 参考) 要扩展或修改继承的方法.属性.索引器或事件的抽象实现或虚实现,必须使用 override 修饰符. C# abstract class ShapesClass { ab ...
随机推荐
- dnslog注入
使用Powershell反弹Meterpreter Shell cloudeye在windows下的应用 今天突发奇想的一个姿势,算不上什么技巧,分享一下吧. 看到有人问过.http://zone.w ...
- navicat 结合快捷键
ctrl+q 打开查询窗口ctrl+/ 注释sql语句ctrl+shift +/ 解除注释ctrl+r 运行查询窗口的sql语句ctrl+shift+r 只运行选中的sql语句F6 打开一个mysql ...
- photoshop
photoshop裁剪 photoshop裁剪快捷键是C键: 使用photoshop裁剪工具裁切出图片的方法: 选择好所需要的图片,右键保存到本地计算机' 在photoshop中打开这张图片(文件/打 ...
- Unity3D UGUI之fbx模型导入问题
在UI层添加了一个fbx的模型,但是在game模式和运行中无法看到这个fbx. Canvas上面有个Render Mode :Screen Space - Overlay.Screen Space ...
- SageCRM 页面加载完后,用下拉框联动修改放大镜字段的取值范围
原理很简单就是修改放大镜属性中的sql. 函数如下:第一个参数是字段的名称.第二个参数是需要控制这个放大镜的sql.可以跟进下拉框的值来组织这个sql. /*--------------- For C ...
- advstringgrid笔记
一下操作是在advstringgrid7.4.6.3版本下有效,advstringgrid控件名设置为 zy 一.属性设置 1.修改单元格内的值:zy.cells[col,row]='value'; ...
- SQL Server2016 新功能实时查询统计信息
SQL Server2016 新功能实时查询统计信息 很多时候有这样的场景,开发抱怨DBA没有调优好数据库,DBA抱怨开发写的程序代码差,因此,DBA和开发都成为了死对头,无法真正排查问题. DBA只 ...
- 跟vczh看实例学编译原理——一:Tinymoe的设计哲学
自从<序>胡扯了快一个月之后,终于迎来了正片.之所以系列文章叫<看实例学编译原理>,是因为整个系列会通过带大家一步一步实现Tinymoe的过程,来介绍编译原理的一些知识点. 但 ...
- 在JQ中关于this
this的相关问题 this指代的是什么 这个应该是比较好理解的,this就是指代当前操作的DOM对象. 在jQuery中,this可以用于单个对象,也可以用于多个对象. $('btn').click ...
- iOS-证书相关
iOS证书相关问题 1.iOS应用真机调试(xcode5),developer证书申请以及真机调试 2.iOS应用发布流程,distribution证书申请以及应用发布到app store 流程 ...