析构函数在对象的生命结束时,会自动调用,大家所熟知的智能指针就是根据析构函数的这种特性而实现的,包括Qt的内存管理机制,也都是利用了析构函数的这一机制来实现的。c++创始人Bjarne Stroustrup在创造析构函数也是出于这种目的的,可见如果析构函数用的好的话,可以省去我们很多工作量,你不再需要手工调用删除对象使用的堆内存,你只需要把要删除的堆内存放入析构函数就行了,因为当对象离开其生命周期的时候,析构函数会自动调用,C++语言规范是这样规定析构函数的调用的:

Destructors are invoked implicitly (1) for a constructed object with static storage duration (3.7.1) at program termination (3.6.3), (2) for a constructed object with automatic storage duration (3.7.2) when the block in which the object is created exits (6.7), (3) for a constructed temporary object when the lifetime of the temporary object ends (12.2), (4) for a constructed object allocated by a newexpression (5.3.4), through use of a deleteexpression (5.3.5), (5) in several situations due to the handling of exceptions (15.3). A program is illformed if an object of class type or array thereof is declared and the destructor for the class is not accessible at the point of the declaration. Destructors can also be invoked explicitly.

大意是:

析构函数可以由以下五种方式隐含调用:

(1)在静态存储区(也即全局对象或静态对象,这个对象放在程序的数据区)里面构造的对象,当程序结束时,对象的析构函数会自动调用。

(2)在自动存储区(也即局部对象,这个对象放在程序的栈里)里面构造的对象离开其区域时,如1个函数内声明的对象,离开函数作用域时,对象的构造函数会自动调用。

(3)临时构造的对象当离开其生命周期时,该对象的析构函数会调用,此处同(2)。

(4)new构造的对象(即对象在堆区),通过delete删除,析构会调用

(5)在try,catch处理异常的情况下,当在try块中对象,因为异常,进入catch分支,会在catch分支中调用构造函数

以上5种是通过编译器生成的默认调用方式,当然了,还有1种就是可以通过显示的方式调用,也即像调用函数一样调用析构函数

有了上面的介绍,接下来进入我们的主题, delete了,却不调用析构函数的情况,这与上面说的C++的第(4)条规范相悖,下面我以一个简单的示例来说明:

示例由7个文件组成,testa.cpp,testa.h, testb.cpp,testb.h,testapp.h,testapp.cpp,main.cpp

  1. testa.h文件
  2. #ifndef _TEST_A_H
  3. #define _TEST_A_H
  4. class CTestA {
  5. public:
  6. CTestA();
  7. ~CTestA();
  8. };
  9. #endif
  1. testa.cpp文件
  2. #include "testa.h"
  3. #include <qdebug.h>
  4. CTestA::CTestA()
  5. {
  6. }
  7. CTestA::~CTestA()
  8. {
  9. qDebug() << "~CTestA()";
  10. }
  1. testb.h文件
  2. #ifndef _TEST_B_H
  3. #define _TEST_B_H
  4. class CTestA;
  5. class CTestB {
  6. public:
  7. static CTestB *getInstance();
  8. CTestA *getTestA();
  9. private:
  10. CTestB();
  11. ~CTestB();
  12. private:
  13. CTestA *pTestA;
  14. static CTestB mSelf;
  15. };
  16. #endif
  1. testb.cpp文件
  2. #include "testa.h"
  3. CTestB CTestB::mSelf;
  4. CTestB *CTestB::getInstance()
  5. {
  6. return &mSelf;
  7. }
  8. CTestB::CTestB()
  9. {
  10. pTestA = new CTestA();
  11. }
  12. CTestB::~CTestB()
  13. {
  14. delete pTestA;
  15. qDebug() << "~CTestB()";
  16. }
  17. CTestA *CTestB::getTestA()
  18. {
  19. return pTestA;
  20. }
  1. testapp.h文件
  2. #ifndef _TEST_APP_H
  3. #define _TEST_APP_H
  4. class CTestA;
  5. class CTestApp {
  6. public:
  7. CTestApp(CTestA *pTestA);
  8. ~CTestApp();
  9. private:
  10. CTestA *pTestA;
  11. };
  12. #endif
  1. testapp.cpp文件
  2. #include "testapp.h"
  3. #include <qdebug.h>
  4. //#include "testa.h"
  5. CTestApp::CTestApp(CTestA *pTestA)
  6. {
  7. this->pTestA = pTestA;
  8. }
  9. CTestApp::~CTestApp()
  10. {
  11. delete pTestA;
  12. qDebug() << "~CTestApp()";
  13. }
  1. main.cpp文件
  2. #include "testb.h"
  3. #include "testcpp.h"
  4. int main(int argc, char *argv[])
  5. {
  6. QApplication app(argc, argv);
  7. CTestB *pTestB = CTestB::getInstance();
  8. CTestApp *pTestApp = new CTestApp(pTestB->getTestA());
  9. delete pTestApp;
  10. return app.exec();
  11. }

下面是输出结果,

~CTestApp()

说明delete pTestA;后没有调用pTestA的析构函数,当把testapp.cpp文件的//#include "testa.h"注释取消,delete后会调用pTestA析构函数,这是去掉注释后的输出结果:

~CTestA()

~CTestApp()

注以上的编译环境是ming32-make,不知道其他的编译环境会不会出现如此问题,这个留给大家去验证。

下面是反汇编代码,这是在testapp.cpp里面加了testa.h的delete pTestA反汇编代码:

delete pTestA;
0x0040255c  <+8>:            	mov    0x8(%ebp),%eax
0x0040255f  <+11>:            	mov    (%eax),%ebx
0x00402561  <+13>:            	test   %ebx,%ebx
0x00402563  <+15>:            	je     0x402575 <~CTestApp+33>
0x00402565  <+17>:            	mov    %ebx,(%esp)
0x00402568  <+20>:            	call   0x403082 <~CTestA>
0x0040256d  <+25>:            	mov    %ebx,(%esp)
0x00402570  <+28>:            	call   0x40aa68 <_ZdlPv>

这是在testapp.cpp里面没有加testa.h的delete pTestA反汇编代码:

delete pTestA;
0x00402550  <+8>:            	mov    0x8(%ebp),%eax
0x00402553  <+11>:            	mov    (%eax),%eax
0x00402555  <+13>:            	mov    %eax,(%esp)
0x00402558  <+16>:            	call   0x40aa48 <_ZdlPv>

可以看到加了testa.h的反汇编中,调用了析构函数~CTestA, call 0x403082 <~CTestA>

http://blog.csdn.net/rabinsong/article/details/9347615

delete了,析构函数却没有调用的更多相关文章

  1. C++析构函数的自动调用问题

    首先要明确一点,系统只会自动释放栈内空间,而堆内空间需要用户自己维护. C++中,除了new来的空间存放在堆内,其他均存放在栈中. 当单纯的创建对象的时候,对象存放在栈中,此时在程序块的}后面,系统会 ...

  2. php析构函数什么时候调用?

    析构函数何时被调用 析构函数在下边3种情况时被调用: 对象生命周期结束,被销毁时: 主动调用delete :(推荐学习:PHP编程从入门到精通) 对象i是对象o的成员,o的析构函数被调用时,对象i的析 ...

  3. new、delete、析构函数、自动类型转换

    new 分配内存,返回指针 new 类型名T (初值列表) 功能:申请用于存放T类型对象的内存空间,并依初值列表赋以初值 结果值: 成功->T类型的指针,指向新分配的内存 失败->0(NU ...

  4. C++析构函数的自动调用(用于父类指针指向子类对象,内存泄漏问题)

    class A {public:A() { printf("A \n"); }~A() { printf(" ~A \n"); } // 这里不管写不写virt ...

  5. delete和析构函数

    new一个类的时候,调用这个类的构造函数,然后在这个类的生命周期内可能会动态生成很多指向堆上的内存,所以应该在析构函数里回收这些内存: 当delete这个类的时候,会首先调用这个类的析构函数,即回收生 ...

  6. C++析构函数的自动调用(析构函数必须是虚拟的,这样删除父类指针指向的子类对象,才能同时调用两者的析构函数,否则就没有机会调用子类析构函数)

    class A {public: A() { printf("A \n"); } ~A() { printf(" ~A \n"); } // 这里不管写不写vi ...

  7. 【日常】C++ 的那些“坑” —— delete 与 析构函数 与 virtual 的 9 个小例子

    C++中有无数的坑,但毕竟-- 今天就踩到了,也算是基本问题了,记录一下,顺便以后可以考考自己.你也可以猜猜答案,大牛绕行. 0x1 先看这个: #include <stdio.h> #i ...

  8. C++继承中析构函数 构造函数的调用顺序以及虚析构函数

    首先说说构造函数.大家都知道构造函数里就能够调用成员变量,而继承中子类是把基类的成员变成自己的成员,那么也就是说子类在构造函数里就能够调用基类的成员了,这就说明创建子类的时候必须先调用基类的构造函数, ...

  9. C++中的内存管理

    在C++中也是少不了对内存的管理,在C++中只要有new的地方,在写代码的时候都要想着delete. new分配的时堆内存,在函数结束的时候不会自动释放,如果不delete我分配的堆内存,则会造成内存 ...

随机推荐

  1. C语言处理CSV文件的方法(一)

    什么是CSV文件 CSV是 Comma-separated values (逗号分隔值)的首字母缩写,它通常是以逗号且不仅限于逗号分隔各个值,我们都叫他CSV. 看下面的例子: China, Shan ...

  2. leetcode Search in Rotated Sorted Array python

      #Suppose a sorted array is rotated at some pivot unknown to you beforehand. #(i.e., 0 1 2 4 5 6 7  ...

  3. hadoop笔记之Hive的数据存储(内部表)

    Hive的数据存储(内部表) Hive的数据存储(内部表) 基于HDFS 可使用hadoop给我们提供的web管理工具查看数据.打开管理工具localhost:9000–>Utilities下的 ...

  4. ParseChat应用源码ios版

    ParseChat是一个完全原生的iPhone应用程序,用于创建实时的.基于文本的Parse聊天室.功能:支持多台设备之间的实时聊天,可动态添加新的聊天室,支持基本配置,可发送和接收音效以及任意大小的 ...

  5. Flex整合Spring

    工程需要整合Spring和Flex,在网上众多方法中找到了下面这种,记录留存. 个人认为该方法更适合在已有Spring框架的工程中添加Flex时使用,对原工程内容(主要指配置文件)改动较小. 1.添加 ...

  6. sshuttle基于VPN的透明代理,安全连接

    sshuttle基于VPN的透明代理, 通过 ssh 创建一条从你电脑连接到任何远程服务器的 VPN 连 sudo sshuttle -r username@sshserver 0.0.0.0/0 - ...

  7. powerdesigner for sqlserver的一些实用配置

    在实用powerdesigner生成sqlserver 数据表时常常遇到一些问题: 1.数据中定义的字段名称相同生成物理模型时会报错. 2.数据各表之间的主键不能定义一样的名称. 我现在的需求是将数据 ...

  8. swift3.0 扩展、协议(4)

    扩展和协议是swift中的两个特性,用于对已有的类型进行扩展和修改. 扩展(extension) 向已经存在的类型添加新的功能(属性.方法.下标脚本等等),扩展使用extension关键字定义,语法 ...

  9. js的replace的用法;

    obj.replace("需要替换的字符串","替换后的字符串")

  10. SQL练习之不破坏应用程序现有查询的修改模式

    当我还是一个菜鸟的时候,当然现在也是,当我的软件需求发生变化时,并且数据库设计同样要求发生变化,我通常会放弃原有的代码(或者对原有的代码进行大改),先在我知道了两个不破坏应用程序现有查询的修改模式,下 ...