[序言]
很久不写C/C++技术贴了,算一下自己用C++也有7~8年了,虽然现在用Delphi比较多,但是对C++还是有一份热情.前段时间在CSDN看到一个帖子,让我感到非常失落, 很多人都没有引用权威文献来针对这个问题进行讨论,如果没有全文文献的引用,那么讨论将会是一个持久战.要结束这种情况,还是以书为准。如果大家都喜欢探讨技术,可以加入我的QQ:643439947一起学习

[建议]
C++是一门非常重要语言且博大精深.没有10年的使用时间和大量C++的书籍阅读,最好不要轻易去探讨C++某些特性,不然真的是那着石头砸自己的脚.就因为这些原因本人也很少在CSDN解答C++的问题,因为C++实在太多细节要注意了,知道得越多,越觉得自己是C++菜鸟.我很害怕的回答是错误的.

[感谢]
曾半仙, 简约而不简单 这些热心网友提出建议性

[适用范围]
本问题所涉及的知识点太多和范围太广,我特定归类为windows桌面系统. 如果突然有人牵扯到嵌入式系统以及嵌入式编译器,那就真的没完没了.下面是一个牛人看了文件给的思路和范围,可想而知太多不可预测的因素了."你考虑一下嵌入开发环境, 虽然语法上支持, 但是库并没有实现new和delete, 这样就引发了不确定因素, 特别是程序员喜欢模版, 喜欢优化, 想使用内存池的情况 "

[原则]
本人是中立人士,不针对任何人,只针对问题.在分析这个问题我又复习了一边C++.这个问题牵涉到 析构函数 虚函数 构造函数 派生 new/delete 5个主要问题.本着学术认真的态度,我翻阅了如下C++书籍
1> C++ Primer Plus
2> C++编程思想 2卷合订本 新版
3> Effective C++
4> Imperfect C++

[引发问题的CSDN链接]
http://topic.csdn.net/u/20110715/15/7ca1e66b-8a04-4c90-80f0-6265ff0269af.html?91968

[还原问题]
class A
{
public:
    A(){} ;
    ~A() {} ; // Ooops must use virtual ~A()
} ;

class B : public A
{
public:
    B(){} ;
    ~B() {} ;
} ;

int main()
{
    A *pclass_A = new B ; // 创建一个B对象指针 隐性转换为 A*
                          //  这里我们需要注意这个转换涉及到一个概念叫: Upcast 中文翻译叫:向上类型转换
    delete pclass_A ;
    pclass_A= NULL ;
    return 0;
}

[分析结论]
就这段代码本身而言我看了4本书也没有很明确的说到这样的写法就会有泄漏.但可以确定这样的写法是一个隐性错误,已违反C++的继承规则和违背继承的实现原理机制.
详细请看:http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7

"....不把析构函数设为虚函数是一个隐性的错误,因为它常常不会对程序有直接影响。但要注意它不知不觉得引入存储器泄漏(关闭程序是内存为释放)。同样,这样的析构操作还有可能掩盖发生的问题...."(摘自: C++编程思想 2卷合订本 第387页)。这句话虽然很短,但是解答了我们很多疑问.

1> “如果你不使用虚析构函数,不会对程序有直接影响”.这里的“不会对程序有直接影响”,我们可以认为delete一个基类指针(基类是没有析构函数),不会照成内存泄漏(仅针对上面的代码而言,如果在派生类中有分配堆,那么肯定会有内存泄漏).
这里为什么我们可以认为delete一个基类指针(基类是没有析构函数),不会照成内存泄漏呢?这就是C++的new 和 delete 的特有机制和职责了.下面看这句话:
"....当在堆栈里主动创建对象时,对象的大小和它们的声明周期被准确地内置在生成的代码里,这是因为编译器知道确切的类型,数量和范围....."(摘自: C++编程思想 2卷合订本 第318页的)这里非常明确的告诉我们,会知道确切的"类型,数量和范围",注意这里有"范围",因此可以推断通过基类指针进行delete,是不会对“不会对程序有直接影响”(备注:请谅解,我没敢直接说不会有内存泄漏,因为我没有能跟编译器厂商求证,但我认为是"应该"不会造成内存泄漏).

2>"但要注意它不知不觉地引入存储器泄漏"这句话又针对前句话做了补充,特别强调了"不知不觉地"+"引入"+"存储器泄漏".很明显的说明了,如果会发生泄漏,那就是外部人为造成的,比如的B类内部中使用了new操作,比如申请10个字节char *char_A = new char[10],那么根据“C++的继承规则和继承的实现原理机制”如果你不把基类的析构函数声明并定义为virtual,那么B类在释放的时候,没法做尾场清理的.比如前面的 new char[10]不能被释放.

额外讨论: 在类继承机制中,构造函数和析构函数具有一种特别机制叫 “层链式调用通知”,这个机制原理是建立在 “vpointer” “VPTR” “VTABLE”这种东西(摘自: C++编程思想 2卷合订本 第369页)(备注:层链式调用通知是我个人理解并总结的词汇.大家可以通过阅读 C++编程思想 2卷合订本 第385页).
流程是这样:在构造一个有类继承机制的类,比如上面的类B,那么会先调用A类的构造,A构造完成之后在调用B类的构造函数,达到"由里向外"通知调用的效果.那么释放一个有类继承机制的类,那么会调用B类的析构函数, 再调用A类的析构函数,达到"由外向里"通知通知的效果,那么为了达到这个这种“层链式调用通知”的效果,C++标准规定:基类的析构函数必须声明为virtual, 如果你不声明,那么"层链式调用通知"这样的机制是没法构建起来.从而就导致了基类的析构函数被调用了,而派生类的析构函数没有调用这个问题发生.但这里要特别注意:这种特殊情况下派生类的析构函数没有被调用,有2中情况发生:
1>如果你的派生类内部没有分配任何堆,而只是单一的局部变量,那么根据局部变量和类的生命周期理论,他们是会被释放的,“不会对程序有直接影响”(备注:请谅解,我没敢直接说不会有内存泄漏,因为我没有能跟编译器厂商求证,但我因为是"应该"不会造成内存泄漏),比如本文顶部列举的代码片段.
2>如果你的派生类内部有分配堆,那么派生类就没法通过自身的析构函数进行尾场清理了,比如 delete []a ;

[CSDN网友pengzhixi提供delete标准说明]
delete-expression:
::opt delete cast-expression
::opt delete [ ] cast-expression

In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
翻译如下:
对于delete object;这种形式,如果delete操作的对象的静态类型不同于动态类型,那么该静态类型必须是动态类型的基类,而且该静态类型必须有一个虚析构函数,否则行为未定义。而对于delete[]这种形式,如果静态类型和动态类型不一致,那么行为未定义(即使你静态类型包含虚析构函数,因为对数组用多态就是一个错误)

[问题总结]
如果你特意去做delete object并且属于行为未定义的,那么有没有内存泄漏还是得跟着开发编译器厂商屁股走,他们说有那就是有,他们说没有那就是没有.但是有一点是,我们必须要做到的:在编写C++代码时,不要做不符合标准的事情.至于那些未定义的行为,让编译器开发商去解决或者操心吧.

[结尾]
写这个文章花费了我1个小时,但在写之前,花费了我2个小时去翻阅4本C++书籍重新去消化这个经典问题.

[查阅资料]
1> C++ Primer Plus 里面的 第13章 类继承
2> C++编程思想 2卷合订本 新版 里面的 第13章 动态对象创建 第14章 继承和组合 第15章 多态性和虚函数

转载于:https://www.cnblogs.com/javaexam2/archive/2011/08/07/2632897.html

[C/C++][经典探讨]类继承中,通过基类指针delete释放,是否会造成内存泄漏相关推荐

  1. 多继承中虚基类构造函数的一种调用规则

    规则:如果父类中有虚基类(A),且有一个直接基类(B)是虚基类的子类,那么子类(C或D)若不显式调用虚基类的有参数构造函数,它的直接基类(B)即使在构造列表中调用了非默认构造函数,那么也会直接调用虚基 ...

  2. java 虚基类_C++中虚基类与抽象类的简单理解。

    虚基类   是相对于它的派生类而言的,它本身可以是一个普通的类. 只有它的派生类虚继承它的时候,它才称作虚基类,如果没有虚继承的话,就称为基类.比如类B虚继承于类A,那类A就称作类B的虚基类,如果没有 ...

  3. 避免在派生类中重新定义基类的非虚函数

    我们都知道,在基类中定义虚函数的目的是允许派生类拥有相同接口却可以有不同的实现,通过对象的指针或引用来访问虚函数可以实现运行时的多态.这么说来,在派生类中重定义(override)虚函数是没有任何问题 ...

  4. 不是抽象类的基类不是好基类

    开宗明义:不是抽象类的基类不是好基类.为什么这么说? 基类和派生类的关系有如下几种: 基类可以是具体类.虚类和抽象类三种,对派生类没有要求.其中具体类是没有虚函数的类,其所有方法都提供了具体实现:派生 ...

  5. 派生类类型可以转换为基类类型,反之则不行

    派生类的对象都含有基类对象作为其一部分,我们可以将指向派生类型的引用转换为指向它的基类型的引用,像转换指针一样,我们可以用派生类的对象初始化或赋值基类对象,反之却不行.class base{ publ ...

  6. C++中的虚继承与虚基类

    1.Cpp中的虚继承与虚基类 在多继承时,很容易产生命名冲突的问题,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示: 类A派 ...

  7. C++知识点47——类继承中的类型转换与访问权限控制(下)

    接上一篇文章https://blog.csdn.net/Master_Cui/article/details/109768311 五.派生类向基类转换的可访问性 当一个子类向基类转化时:有以下三条规则 ...

  8. C++知识点46——类继承中的类型转换与访问权限控制(中)

    接上一篇文章https://blog.csdn.net/Master_Cui/article/details/109741735 四.public继承.protected继承和private继承 子类 ...

  9. C++知识点45——类继承中的类型转换与访问权限控制(上)

    一.类的继承与类型转换 1.概述 一般情况,如果想把一个类的指针或引用绑定到另外一个对象上,需要指针或者引用的类型与指向对象一致.但是存在继承关系的类是个例外:可以将基类的指针或者引用指向子类 原因是 ...

  10. C# 类型运算符重载在类继承中的调用测试

    C# 类型运算符重载在类继承中的调用测试 这是一篇晦涩难懂的片面的研究 一,简单的继承层次 class CA {}class CB : CA{}class CC : CB{}}void Test(CA ...

最新文章

  1. 我与我的专业计算机作文500字,我的好朋友——电脑
  2. 手机mvno怎么设置_微信透明背景壁纸怎么弄 手机设置方法教程分享
  3. Meta为元宇宙建全球最快AI超算,1.6万个A100 GPU,英伟达都赚麻了
  4. svn版本库浏览器_svn:版本库xxx不存在||svn:No such revision xxx的问题
  5. VTK:PolyData之MaskPoints
  6. 【GIS风暴】什么是地理空间智能(Geospatial AI)?
  7. 牛客题霸 [最长重复子串] C++题解/答案
  8. php css去除h1样式,HTML中怎么设置h1的字体样式你知道吗?关于设置h1标签的样式详解...
  9. Java 表单提交下拉框_Java实现Layui的form表单动态绑定下拉框
  10. python怎样实现封装_Python底层封装实现方法详解
  11. Android快速开发框架Android-query
  12. 开发过程中解决各种跨域问题
  13. 【CF585-div2:C】Swap Letters(贪心)
  14. android+vmware+wifi,笔记本使用wifi通过vmware workstation+openwrt 实现上网
  15. pmu2008终端服务器,PMU升级指导.doc
  16. 男生说fb是什么梗_男生聊污是什么意思 男生会对谁聊污
  17. 数据挖掘基本流程 CRISP-DM --项目实战总结 可操作性强
  18. Hadoop-5-HDFS
  19. python+django大学生专业社团信息管理系统
  20. 原生JS实现俄罗斯方块

热门文章

  1. java main usage_java-一个简单的访问DB的main方法使用 | 学步园
  2. docker 镜像注册【图文教程】
  3. 使用java的io流编写日志类
  4. 手把手教你Windows环境下配置Git环境
  5. python需要excel基础吗_Python实现和Excel基础功能对应关系
  6. 【渝粤教育】国家开放大学2018年秋季 1248T公共部门人力资源管理 参考试题
  7. 有监督的神经网络模型
  8. 整个社会总嫌自己不够“快”,为啥?
  9. 【Java_Spring】控制反转IOC(Inversion of Control)
  10. 求数组中最大值和次大值