程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> delete了,析構函數卻沒有調用

delete了,析構函數卻沒有調用

編輯:C++入門知識

析構函數在對象的生命結束時,會自動調用,大家所熟知的智能指針就是根據析構函數的這種特性而實現的,包括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++的第(3)條規范相悖,下面我以一個簡單的示例來說明:

示例由5個文件組成,testa.cpp,testa.h, testb.cpp,testb.h,main.cpp


[html]
testa.h文件 
#ifndef _TEST_A_H 
#define _TEST_A_H 
class CTestA { 
public: 
    CTestA(); 
    ~CTestA(); 
}; 
#endif 

testa.h文件
#ifndef _TEST_A_H
#define _TEST_A_H
class CTestA {
public:
    CTestA();
    ~CTestA();
};
#endif
[html]
testa.cpp文件 
 
#include "testa.h" 
#include <qdebug.h> 
CTestA::CTestA() 

 

 
CTestA::~CTestA() 

    qDebug() << "~CTestA()"; 

testa.cpp文件

#include "testa.h"
#include <qdebug.h>
CTestA::CTestA()
{

}

CTestA::~CTestA()
{
    qDebug() << "~CTestA()";
}

[html]
testb.h文件 
 
#ifndef _TEST_B_H 
#define _TEST_B_H 
class CTestA; 
class CTestB { 
public: 
    static CTestB *getInstance(); 
    CTestA *getTestA(); 
 
private: 
    CTestB(); 
    ~CTestB(); 
 
private: 
    CTestA *pTestA; 
    static CTestB mSelf; 
}; 
#endif 

testb.h文件

#ifndef _TEST_B_H
#define _TEST_B_H
class CTestA;
class CTestB {
public:
    static CTestB *getInstance();
    CTestA *getTestA();

private:
    CTestB();
    ~CTestB();

private:
    CTestA *pTestA;
    static CTestB mSelf;
};
#endif
[html]
testb.cpp文件 
#include "testb.h" 
#include "testa.h" 
#include <qdebug.h> 
CTestA::CTestA() 

 

 
CTestA::~CTestA() 

    qDebug() << "~CTestA()"; 

testb.cpp文件
#include "testb.h"
#include "testa.h"
#include <qdebug.h>
CTestA::CTestA()
{

}

CTestA::~CTestA()
{
    qDebug() << "~CTestA()";
}

[html]
testapp.h文件 
 
#ifndef _TEST_APP_H 
#define _TEST_APP_H 
class CTestA; 
class CTestApp { 
public: 
    CTestApp(CTestA *pTestA); 
    ~CTestApp(); 
private: 
    CTestA *pTestA; 
}; 
#endif 

testapp.h文件

#ifndef _TEST_APP_H
#define _TEST_APP_H
class CTestA;
class CTestApp {
public:
    CTestApp(CTestA *pTestA);
    ~CTestApp();
private:
    CTestA *pTestA;
};
#endif
[html]
testapp.cpp文件 
 
#include "testapp.h" 

testapp.cpp文件

#include "testapp.h"[html] view plaincopyprint?<SPAN style="FONT-FAMILY: arial">#include <qdebug.h></SPAN> 

#include <qdebug.h>[html] view plaincopyprint?<SPAN style="FONT-FAMILY: arial">//#include "testa.h"</SPAN> 

//#include "testa.h"[html] view plaincopyprint?CTestApp::CTestApp(CTestA *pTestA) 

    this->pTestA = pTestA; 

 
CTestApp::~CTestApp() 

    delete pTestA; 
    qDebug() << "~CTestApp()"; 

CTestApp::CTestApp(CTestA *pTestA)
{
    this->pTestA = pTestA;
}

CTestApp::~CTestApp()
{
    delete pTestA;
    qDebug() << "~CTestApp()";
}

[html]
main.cpp文件 
#include "testb.h" 
#include "testcpp.h" 
int main(int argc, char *argv[]) 

    QApplication app(argc, argv); 
    CTestB *pTestB = CTestB::getInstance(); 
    CTestApp *pTestApp = new CTestApp(pTestB->getTestA()); 
    delete pTestApp; 
    return app.exec(); 

main.cpp文件
#include "testb.h"
#include "testcpp.h"
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    CTestB *pTestB = CTestB::getInstance();
    CTestApp *pTestApp = new CTestApp(pTestB->getTestA());
    delete pTestApp;
    return app.exec();
}下面是輸出結果,

~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),%eax0x0040255f  <+11>:             mov    (%eax),%ebx0x00402561  <+13>:             test   %ebx,%ebx0x00402563  <+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),%eax0x00402553  <+11>:             mov    (%eax),%eax0x00402555  <+13>:             mov    %eax,(%esp)0x00402558  <+16>:             call   0x40aa48 <_ZdlPv>可以看到加了testa.h的反匯編中,調用了析構函數~CTestA, call 0x403082 <~CTestA>

 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved