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 ...
随机推荐
- Devexpress Ribbon
http://www.cnblogs.com/liwei81730/archive/2011/12/21/2296203.html 可查看此处.
- 让代码重构渐行渐远系列(3)——string.Equals取代直接比较与非比较
重构背景及原因 最近由于项目组的人员在不断扩充,导致项目中代码风格各异,大有百花齐放甚至怒放之势.考虑到团队的生存与发展,经过众人多次舌战之后,最终决定项目组根据业务分成几个小分队,以加强团队管理与提 ...
- 微软StockTrader应用程序
这是一个采用 .NET Enterprise Application Server 技术的端到端示例应用程序.应用程序代码可以从 这里 下载. 代码中演示了WCF服务和移动开发,包括用Xamarin ...
- Javascript设置对象属性为"只读"
有时为了保护某些属性,让其无法被更改,我们会把他们设置为常量. 在某些语言里面,也许会用const来实现这样的功能.本文讲述如何在Javascript中实现这样的功能. 方法一: var myObje ...
- VBA批量查找和复制文件
Function findAndCopy(srcFile As String, destFile As String, cmdFile As String) Dim WSH As Object, wE ...
- C语言 · 冒泡排序
for(int k=0;k<N;k++) { for(int j=k+1;j<N;j++){ if(a[k]>a[j]){ int t = a[k]; a ...
- Java 8函数编程轻松入门(二)Stream的使用
在C#中,微软基于IEnumerable接口,提供许多便捷的扩展方法,便于实际的开发.在Java 1.8中,Collection接口新增了default stream方法.我们可以针对java集合,在 ...
- [Hadoop大数据]——Hive初识
Hive出现的背景 Hadoop提供了大数据的通用解决方案,比如存储提供了Hdfs,计算提供了MapReduce思想.但是想要写出MapReduce算法还是比较繁琐的,对于开发者来说,需要了解底层的h ...
- iOS---iOS9搜索功能
前言 在iOS9之前我们只能使用Spotlight来搜索应用名称来打开指定App,而其他的内容都是提供给系统使用(信息,联系人,邮件等).在iOS9以后Apple允许开发者设置应用中任意内容可以被Sp ...
- 一个美术需求引发的Custom Inspector
需求 Editor模式下,在运行或者非运行状态下,能够按照指定的变化率来自动改变material中属性数值. 需求分析 如何在Editor模式下获得一个游戏对象及其组件,尤其是在非运行状态下?我们知道 ...