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

深入研究析構函數

編輯:C++入門知識

 析構函數是C++中一個神奇的部分,在調用析構函數時,並不需要像普通函數一樣寫出函數調用的代碼,而是由編譯器將析構函數插入到程序中合適的調用地方。如果你不清楚這些插入析構函數的地方,就會出現一些很難解決的錯誤。
    
    在分析析構函數的執行時,一個經典的示例是全局變量的析構過程。我們來考慮下面的代碼:
    

  
    當運行這個程序時,將會在調用g_pUnKnow的析構函數時發生崩潰。其中的原因是:全局變量的析構函數是主程序退出時才調用的,而在主程序退出時,COM環境也將被卸載。COM的卸載工作包括釋放在初始化COM環境時所加載的動態鏈接庫。然後當你釋放全局變量指針時,程序將崩潰,因為程序試圖與一個不存在的DLL通信。         這個問題並不僅限於全局變量,有時候局部變量也會出現這樣的情況:
   

 
    這段程序非常簡單,在代碼中有一個錯誤。智能指針的析構函數在什麼時候被調用?答案是:當智能指針超出作用域的時候被調用。由於已經卸載了COM環境,當你再試圖訪問一個指向COM對象的指針時,將發生與前面一樣的錯誤。         要修正這個問題,就必須在CoUninitialize之前釋放左右的COM指針。方法就是加入一個看上去似乎沒有必要的作用域:
   

 
    不過你要確保在代碼中留下相應的注釋,確保不會被閱讀這段代碼的人刪除這兩個"多余的"大括號。         有些人可能會認為這個解決方案很不直觀。那麼下面將給出另外一個解決方案:將CoUninitialize放在某個對象的析構函數中。
   

 
    現在即使你將智能指針放在同樣的作用域中依然可行。只要保證智能指針是位於CCoInitialize對象之後:
 

    
    這段代碼是沒有問題的,因為自動儲存類型對象在調用析構函數時的順序與聲明這些對象的順序是相反的。所以對象p首先被析構,然後才是對象init.         到目前為止,我們已經看到了一些在錯誤時刻調用的析構函數。現在,再來看一些不會被調用的析構函數。         假設有一個ObjectLock類,在這個類的構造函數中將獲得一個鎖,並在其析構函數中釋放這個鎖:
   

 
    在這段代碼中,第一部分的操作是在沒有加鎖的情況下完成的,而第二部分操作則是在加鎖的情況下完成的。當函數返回時,這個鎖將自動被釋放。然而如果在這個函數中增加了下面這樣一行代碼:
   

 
    這段代碼的意思是:如果對象被取消了,就提前退出線程。但是ObjectLock對象的析構函數在什麼時候被調用呢?         這個析構函數將在return語句中運行,因為此時ObjectLock對象已經超出作用域。然而,在調用ExitThread函數之前,析構函數是不會被調用的。結果就是,程序使一個對象被永久鎖定。         有些人可能會爭論:調用ExitThread是不好的變成習慣,我們應該通過執行到線程函數的最後來結束一個線程。然而,有一種情況你必須通過退出函數來退出線程:如果是一個工作線程,雖然這個線程的生命周期並沒有被進程顯示管理,但線程的代碼是在一個DLL中。這種情況下,標准的做法是:當工作線程啟動的時候,調用LoadLibrary(Load Count)函數來增加DLL的加載計數,而當工作線程結束時,調用FreeLibraryAndExitThread函數(當然,你也可以同樣使用GetModuleHandleEx函數來增加加載計數)。如果使用這種方法,線程看起來就像這樣:
   

 
    其中g_hinst是一個全局變量,在這個變量中保存的是DLL的實例句柄。在這種情況下,你會遇到和前面同樣的問題:ObjectLock的析構函數是在函數的括號結束處執行的,但是FreeLibraryAndExitThread函數退出線程並且不會再回到函數中。因此,析構函數永遠不會被執行。同樣,我們依然可以使用一個嵌套的作用域來強制析構函數的執行:

 
  
摘自  家裡蹲博客 

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