2020-12-14(全局/静态对象的构造函数和析构函数调用的时机以及地址)
一般的對象實例化在什么時候實例化的呢?
是不是在main函數運行到那里的時候,然后創建對象,會調用類里面的構造函數。
那當我們遇到全局/靜態對象的時候,它是不是也是需要在main函數里面慢慢構造呢?
答案是 :
不是的。
全局/靜態對象 的構造函數調用實在main函數之前的。
有人在疑問?main函數之前還有函數?不是從main函數開始運行的嗎?
全局對象所在的內存地址空間為全局數據區,而局部對象的內存地址空間在棧中,他們觸發構造函數和析構函數時機不同。
全局/靜態對象構造函數實現:
在啟動函數mainCRTStartup中有個_cinit,全局對象的構造函數就是在此函數中實現的,
在函數_cinit的_initterm函數調用中,初始化了全局對象。_initterm實現代碼片段如下:
當pfbegin不為NULL時進入if語句塊。執行(**pfbegin)();
后并不會進入全局對象的構造函數中,而是進入編譯器提供的構造代理函數中,由一個負責全局對象的構造代理函數完成對全局構造函數的調用過程。
構造代理函數代碼如下:
由于構造函數需要傳遞對象的首地址作為this指針,而且構造函數可以帶各類參數,因此編譯器將為每個全局對象生成一段傳遞this指針和參數的代碼,然后使用無參的代理函數去調用構造函數。
全局/靜態對象析構函數實現:
全局/靜態對象相同,其構造函數在函數_cinit的第二個_initterm調用中被構造。他們的析構函數的調用時機是在main函數執行完畢之后。既然構造函數出現在初始化過程中,對應的析構函數就會出現在程序結束出。我們來看一下mainCRTStartup函數,它在調用main函數結束后使用了exit用來終止程序,如下:
在main函數調用結束后,由exit來結束進程,從而終止程序的運行。全局對象的析構函數的調用也在其中,由exit函數內的doexit實現,關鍵代碼如下:
if(_onexitbegin)//_onexitbegin為函數指針數組的首地址 { _PVFV * pfend=_onexitend; //__onexitbegin為函數指針數組的尾地址 while(--pend >=__onexitbegin)//從后向前依次釋放全局對象if(*pend!=NULL)(**pend)();//調用數組中保存的函數 }__onexitbegin指向一個指向數組,該數組中保存著各類資源釋放時的函數的首地址。編譯器實在何時生成這樣一個數組的呢?
全局構造函數的調用是在_cinit函數的第二個_initterm函數內完成,而在第二個_initterm函數的初始化函數指針數組。在執行每個全局對象構造代理函數時都會執行對象的構造函數,然后使用atexit注冊析構代理函數。
舉例:
如果定義一個全局對象CMyString G_MyStringTwo;,該對象的全局析構函數代理函數的分析如下所示:
;該代理函數由編譯器添加,無源碼對照
;函數入口對照
由于函數數組中保存的析構代理函數被定義為無參函數,因此在調用析構函數時無法傳遞this指針。于是編譯器需要為每個全局變量和靜態對象建立一個中間代理的析構函數,用于傳入全局對象的this指針。
總結
以上是生活随笔為你收集整理的2020-12-14(全局/静态对象的构造函数和析构函数调用的时机以及地址)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++对象长度之空类(1)
- 下一篇: 对象作为函数参数