c++ delete 调用析构函数问题。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++ delete 调用析构函数问题。相关的知识,希望对你有一定的参考价值。

参考技术A delete是手动释放new出来的内存空间,如果你不delete
new出来的空间,那么这个内存会被一直占用,直到程序退出。
析构函数是自动调用的。
不要去理会谁先被释放,这个例子没有什么意思。
顺便说句,玩的时间长了就理解了
参考技术B #include<iostream>
using
namespace
std;
class
A

public:
~A()

cout
<<
"~A"
<<
endl;

;
int
main()

A*
ra
=
new
A[2]
A(),A()
;
cout
<<
"ddd"
<<
endl;
delete[]
ra;
system("pause");
return
0;

很明显,A*
ra
=
new
A[2]
A(),A()
;这句是创建两个临时对象然后复制到ra指向的内存段上。复制之后立即释放临时对象,最后两次析构则是delete[]调用的。
但是很奇怪的是,如果定义除拷贝构造函数之外的任意一种构造函数,就会触发VS2015编译器优化,使得临时对象直接构造在ra指向的内存段上。这样前2次析构就不会发生。个人认为这纯粹取决于编译器实现,感觉像是一种BUG,也许也有一些道理,自动生成的默认构造函数和自己编写的构造函数在对待成员变量初始化方面往往不太一致,也许编译器考虑这种情况。

delete了,析构函数却没有调用

析构函数在对象的生命结束时,会自动调用,大家所熟知的智能指针就是根据析构函数的这种特性而实现的,包括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

 

[html] view plain copy
 
  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  

 

[html] view plain copy
 
  1. testa.cpp文件  
  2.   
  3. #include "testa.h"  
  4. #include <qdebug.h>  
  5. CTestA::CTestA()  
  6. {  
  7.   
  8. }  
  9.   
  10. CTestA::~CTestA()  
  11. {  
  12.     qDebug() << "~CTestA()";  
  13. }  

 

[html] view plain copy
 
  1. testb.h文件  
  2.   
  3. #ifndef _TEST_B_H  
  4. #define _TEST_B_H  
  5. class CTestA;  
  6. class CTestB {  
  7. public:  
  8.     static CTestB *getInstance();  
  9.     CTestA *getTestA();  
  10.   
  11. private:  
  12.     CTestB();  
  13.     ~CTestB();  
  14.   
  15. private:  
  16.     CTestA *pTestA;  
  17.     static CTestB mSelf;  
  18. };  
  19. #endif  

 

[html] view plain copy
 
  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.   
  13. CTestB::~CTestB()  
  14. {  
  15.     delete pTestA;  
  16.     qDebug() << "~CTestB()";  
  17. }  
  18.   
  19. CTestA *CTestB::getTestA()  
  20. {  
  21.     return pTestA;  
  22. }  

 

 

[html] view plain copy
 
  1. testapp.h文件  
  2.   
  3. #ifndef _TEST_APP_H  
  4. #define _TEST_APP_H  
  5. class CTestA;  
  6. class CTestApp {  
  7. public:  
  8.     CTestApp(CTestA *pTestA);  
  9.     ~CTestApp();  
  10. private:  
  11.     CTestA *pTestA;  
  12. };  
  13. #endif  

 

[html] view plain copy
 
  1. testapp.cpp文件  
  2.   
  3. #include "testapp.h"  
  4. #include <qdebug.h>  
  5. //#include "testa.h"  
  6. CTestApp::CTestApp(CTestA *pTestA)  
  7. {  
  8.     this->pTestA = pTestA;  
  9. }  
  10.   
  11. CTestApp::~CTestApp()  
  12. {  
  13.     delete pTestA;  
  14.     qDebug() << "~CTestApp()";  
  15. }  

 

 

[html] view plain copy
 
  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

以上是关于c++ delete 调用析构函数问题。的主要内容,如果未能解决你的问题,请参考以下文章

c++ 析构函数 是在啥时候执行

C++ 【错误求帮改】构造函数、析构函数,delete等编程时的问题

delete了,析构函数却没有调用

C++ 析构函数何时被调用?

C++ 析构函数何时被调用?

c++面试题1