全局对象_C++全局变量初始化
c++ 全局變量初始化的一點總結
對于C語言的全局和靜態變量,不管是否被初始化,其內存空間都是全局的;如果初始化,那么初始化發生在任何代碼執行之前,屬于編譯期初始化。由于內置變量無須資源釋放操作,僅需要回收內存空間,因此程序結束后全局內存空間被一起回收,不存在變量依賴問題,沒有任何代碼會再被執行!
C++引入了對象,這給全局變量的管理帶領新的麻煩。C++的對象必須有構造函數生成,并最終執行析構操作。由于構造和析構并非分配內存那么簡單,可以說相當復雜,因此何時執行全局或靜態對象(C++)的構造和析構呢?這需要執行相關代碼,無法在編譯期完成,因此C++標準規定:全局或靜態對象當且僅當對象首次用到時才進行構造,并通過atexit()來管理對象的生命期,在程序結束之后(如調用exit,main),按FILO順序調用相應的析構操作!
全局變量、文件域的靜態變量和類的靜態成員變量在main執行之前的靜態初始化過程中分配內存并初始化;局部靜態變量(一般為函數內的靜態變量)在第一次使用時分配內存并初始化。這里的變量包含內置數據類型和自定義類型的對象。
函數內的靜態對象,初始化是在第一次運行到那里的時候才初始化的。假設有一個函數testFun,大約是下面這樣
void testFun() { static std::string str("Test string"); }那么函數里面那個str,要第一次運行testFun的時候才會被初始化。
初始化的順序
對于出現在同一個編譯單元內的全局變量來說,它們初始化的順序與他們聲明的順序是一致的(銷毀的順序則反過來),而對于不同編譯單元間的全局變量,c++ 標準并沒有明確規定它們之間的初始化(銷毀)順序應該怎樣,因此實現上完全由編譯器自己決定,一個比較普遍的認識是:不同編譯單元間的全局變量的初始化順序是不固定的,哪怕對同一個編譯器,同一份代碼來說,任意兩次編譯的結果都有可能不一樣[1]。
因此,一個很自然的問題就是,如果不同編譯單元間的全局變量相互引用了怎么辦?
當然,最好的解決方法是盡可能的避免這種情況.
幾個技巧
好吧,我承認總有那么一些特殊的情況,是需要我們來處理這種在全局變量的初始化函數里竟然引用了別的地方的全局變量的情況,比如說在全局變量的初始化函數里調用了 cout, cerr 等(假設是用來打 log, 注意 cout 是標準庫里定義的一個全局變量)[2],那么標準庫是怎樣保證 cout 在被使用前就被初始化了呢? 有如下幾個技巧可以介紹一下。
Construct On First Use
該做法是把對全局變量的引用改為函數調用,然后把全局變量改為函數內的靜態變量:
int get_global_x() {static X x;return x.Value(); }這個方法可以解決全局變量未初始化就被引用的問題,但還有另一個對稱的問題它卻沒法解決,函數內的靜態變量也屬于 variables with static storage, 它們析構的順序在不同的編譯單元間也是不確定的,因此上面的方法雖然必然能保證 x 的初始化先于其被使用,但卻沒法妥善處理,如果 x 析構了 get_global_x() 還被調用這種可能發生的情況。
一個改進的做法是把靜態變量改為如下的靜態指針:
int get_global_x() {static X* x = new X;return x->Value(); }這個改進可以解決前面提到的 x 析構后被調用的問題,但同時卻也引入了另一個問題: x 永遠都不會析構了,內存泄漏還算小問題或者說不算問題,但如果 x 的析構函數還有事情要做,如寫文件清理垃圾什么的,此時如果對象不析構,顯然程序的正確性都無法保證.
這個問題在 gcc c++ 的標準庫里也沒有得到解決,有興趣的可以看看這個討論。
--more
Storage duration
All objects in a program have one of the following storage durations:
- automatic storage duration. The storage for the object is allocated at the beginning of the enclosing code block and deallocated at the end. All local objects have this storage duration, except those declared static, extern or thread_local.
- static storage duration. The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern.
- thread storage duration. The storage for the object is allocated when the thread begins and deallocated when the thread ends. Each thread has its own instance of the object. Only objects declared thread_local have this storage duration. thread_local can appear together with static or extern to adjust linkage.
(since C++11)
- dynamic storage duration. The storage for the object is allocated and deallocated per request by using dynamic memory allocation functions.
初始化沒有順序。一個全局變量可能指到另一個全局變量。但那個全局變量可能還有初始化或者已經end了。
各全局變量的析構函數是順序調用的,與調用構造函數的順序是相反的。這就保證做到“先構造的全局類變量后析構。”
Dynamic initialization is not ordered across translation units, and neither is destruction (except that destruction happens in reverse order of initialization).
When one initialization refers to another variable with static storage duration, it is possible that this causes an object to be accessed before its lifetime has begun (or after its lifetime has ended). Moreover, when a program starts threads that are not joined at exit, those threads may attempt to access objects after their lifetime has ended if their destructor has already run.
總結
以上是生活随笔為你收集整理的全局对象_C++全局变量初始化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: rust模组服如何切换标准服_送给玩模组
- 下一篇: java mvc mvp mvvm_一篇