亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

蟲蟲首頁| 資源下載| 資源專輯| 精品軟件
登錄| 注冊

您現在的位置是:首頁 > 技術閱讀 >  重點關注:C++全局變量初始化的大坑

重點關注:C++全局變量初始化的大坑

時間:2024-02-13


大家好,我是程序喵!



最近發現的一篇關于C++全局變量初始化的文章(原文作者twoon),分享給大家。



注意:本文所說的全局變量指的是 variables with static storage,措詞來自 c++ 的語言標準文檔。


什么時候初始化

根據 C++ 標準,全局變量的初始化要在 main 函數執行前完成,常識無疑,但是這個說法有點含糊,main 函數執行前到底具體是什么時候呢?是編譯時還是運行時?答案是既有編譯時,也可能會有運行時(seriously), 從語言的層面來說,全局變量的初始化可以劃分為以下兩個階段(c++11 N3690 3.6.2):

  1. static initialization: 靜態初始化指的是用常量來對變量進行初始化,主要包括 zero initialization 和 const initialization,靜態初始化在程序加載的過程中完成,對簡單類型(內建類型,POD等)來說,從具體實現上看,zero initialization 的變量會被保存在 bss 段,const initialization 的變量則放在 data 段內,程序加載即可完成初始化,這和 c 語言里的全局變量初始化基本是一致的。

  2. dynamic initialization:動態初始化主要是指需要經過函數調用才能完成的初始化,比如說:int a = foo(),或者是復雜類型(類)的初始化(需要調用構造函數)等。這些變量的初始化會在 main 函數執行前由運行時調用相應的代碼從而得以進行(函數內的 static 變量除外)。


需要明確的是:靜態初始化執行先于動態初始化! 只有當所有靜態初始化執行完畢,動態初始化才會執行。顯然,這樣的設計是很直觀的,能靜態初始化的變量,它的初始值都是在編譯時就能確定,因此可以直接 hard code 到生成的代碼里,而動態初始化需要在運行時執行相應的動作才能進行,因此,靜態初始化先于動態初始化是必然的。


初始化的順序

對于出現在同一個編譯單元內的全局變量來說,它們初始化的順序與他們聲明的順序是一致的(銷毀的順序則反過來),而對于不同編譯單元間的全局變量,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 的析構函數還有事情要做,如寫文件清理垃圾什么的,此時如果對象不析構,顯然程序的正確性都無法保證。


Nifty counter.

完美一點的解決方案是 Nifty counter, 現在 GCC 采用的就是這個做法[3][7]。假設現在需要被別處引用的全局變量為 x, Nifty counter 的原理是通過頭文件引用,在所有需要引用 x 的地方都增加一個 static 全局變量,然后在該 static 變量的構造函數里初始化我們所需要引用的全局變量 x,在其析構函數里再清理 x,示例如下:

// global.h

#ifndef _global_h_
#define _global_h_


extern X x;

class initializer
{

public:
initializer()

{
if (s_counter_++ == 0) init();
}

~initializer()
{
if (--s_counter_ == 0) clean();
}

private:
void init();
void clean();

static int s_counter_;
};

static initializer s_init_val;

#endif

相應的 cpp 文件:

// global.cpp

#include "global.h"

static X x;

int initializer::s_counter_ = 0;

void initializer::init()
{
new(&x) X;
}

void initializer::clean()
{
(&x)->~X();
}


代碼比較直白,所有需要引用 x 的地方都需要引用 global.h 這個頭文件,而一旦引入了該頭文件,就一定會引入 initializer 類型的一個靜態變量 s_init_val, 因此雖然不同編譯單元間的初始化順序不確定,但他們都肯定包含有 s_init_val,因此我們可以在 s_init_val 的構造函數里加入對 x 的初始化操作,只有在第一個 s_init_val 被構造時才初始化 x 變量,這可以通過 initializer 的靜態成員變量來實現,因為 s_counter_ 的初始化是靜態初始化,能保證在程序加載后就完成了。


初始化 x 用到了 placement new 的技巧,至于析構,那就是簡單粗暴地直接調用析構函數了,這一段代碼里的技巧也許有些難看,但都是合法的,當然,同時還有些問題待解決:


首先,因為 x 是復雜類型的變量,它有自己的構造函數,init() 函數初始化 x 之后,程序初始化 x 所在的編譯單元時,x 的構造函數還會被再調用一次,同理 x 析構函數也會被調用兩次,這顯然很容易引起問題,解決的方法是把 x 改為引用:

// global.cpp

#include "global.h"

// need to ensure memory alignment??
static char g_dummy[sizeof(X)];

static X& x = reinterpret_cast<X&>(g_dummy);

int initializer::s_counter_ = 0;

void initializer::init()
{
new(&x) X;
}

void initializer::clean()
{
(&x)->~X();
}

其中 static X& x = reinterpret_cast<X&>(g_dummy); 這一行是靜態初始化,因為 g_dummy 是編譯時就確定了的(引用是簡單類型且以常量為初始值),而 x 只是一個強制轉化而來的引用,編譯器不會生成調用 x 構造函數和析構函數的代碼。通過上面的修改,這個方案已經比較完美了,但遺憾的是它也不是 100% 正確的,這個方案能正確工作的前提是:所有引用 x 的地方都會 include 頭文件 global.h,但如果某一個全局變量 y 的初始化函數里沒有直接引用 x, 而是間接調用了另一個函數 foo,再通過 foo 引用了 x,此時就可能出錯了,因為 y 所在的編譯單元里可能并沒有直接引用 x,因此很有可能就沒有 include 頭文件 global.h,那么 y 的初始化就很有可能發生在 x 之前。。。


這個問題在 gcc c++ 的標準庫里也沒有得到解決,有興趣的可以看看這個討論。


[參考]

http://isocpp.org/wiki/faq/ctors#static-init-order
https://gcc.gnu.org/onlinedocs/libstdc++/manual/io.html#std.io.objects
https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-3.4/ios__init_8cc-source.html
https://social.msdn.microsoft.com/Forums/vstudio/en-US/637a4c27-3e30-4b88-b36d-b5b720cf0d04/why-are-cout-cin-initialized-once-and-only-once-given-the-scheme-below-in-the-iostream?forum=vclanguage
http://www.petebecker.com/js/js199905.html
http://blogs.msdn.com/b/ce_base/archive/2008/06/02/dynamic-initialization-of-variables.aspx
http://cs.brown.edu/people/jwicks/libstdc++/html_user/globals__io_8cc-source.html


本文作者:twoon

摘自:https://www.cnblogs.com/catch/p/4314256.html




往期推薦


1、少寫點
if-else吧,它的效率有多低你知道嗎?
2、年度原創好文匯總
3、深度好文|面試官:進程和線程,我只問這19個問題
4
他來了,他來了,C+
+17新特性精華都在這了
5一文讓你搞懂設計模式
6、C++11新特性,所有知識點都在這了!





C++學習資料免費獲取方法:關注程序喵大人,后臺回復“程序喵”即可免費獲取40萬字C++進階獨家學習資料。



亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频
欧美专区亚洲专区| 亚洲国产精品t66y| 香蕉久久精品日日躁夜夜躁| 亚洲乱码一区二区| 99综合电影在线视频| 一二三区精品福利视频| 亚洲欧美日韩中文播放| 久久久一本精品99久久精品66| 久久精品综合| 欧美色欧美亚洲另类二区| 国产精品网曝门| 亚洲高清在线观看一区| 亚洲视频在线看| 狼人社综合社区| 国产精品v片在线观看不卡| 韩国一区电影| 夜夜精品视频| 久久国产精品毛片| 欧美另类在线播放| 国外成人在线视频| 日韩视频精品| 蘑菇福利视频一区播放| 欧美视频一区二区三区…| 国产一区视频网站| 亚洲一区二区综合| 免费成人毛片| 国产自产v一区二区三区c| 午夜精品久久| 久久夜色精品国产欧美乱| 欧美三级电影大全| 一区一区视频| 亚洲免费中文字幕| 欧美精品一区二区三区视频| 国语自产精品视频在线看一大j8| 91久久久亚洲精品| 久久久999精品| 国产精品www994| 亚洲国产小视频| 久久综合久久久| 国产亚洲欧美色| 亚洲欧美乱综合| 国产精品福利av| 亚洲视频www| 欧美性猛交一区二区三区精品| 亚洲国产精品一区二区尤物区| 久热精品视频在线观看| 国产视频不卡| 欧美伊人久久大香线蕉综合69| 国产精品日本欧美一区二区三区| 欧美日韩中文字幕| 亚洲永久免费观看| 欧美视频在线观看视频极品| 欧美精品一区二区精品网| 亚洲国产成人在线| 欧美成人自拍| 黄色一区二区在线| 久久久久久久一区| 禁断一区二区三区在线| 久久躁狠狠躁夜夜爽| 亚洲国产你懂的| 国产精品一区二区在线观看网站| 99国产精品国产精品久久| 欧美精品久久久久久久免费观看 | 国产精品一级在线| 亚洲主播在线播放| 国产女主播一区| 亚洲午夜av| 国产精品一区二区久久精品| 亚洲欧美在线磁力| 激情久久久久久久| 久久综合综合久久综合| 国产亚洲欧美中文| 久久免费国产精品| 亚洲毛片在线观看| 欧美精品一二三| 欧美一区二区日韩| 国内久久精品| 久久久无码精品亚洲日韩按摩| 国产一区二区福利| 浪潮色综合久久天堂| 这里只有精品丝袜| 黄色精品网站| 欧美日韩高清一区| 欧美在线网站| 1000精品久久久久久久久| 免费久久99精品国产| 日韩视频在线播放| 国产精品一区二区黑丝| 亚洲福利视频免费观看| 国产精品第一页第二页第三页| 久久亚洲欧美国产精品乐播| 亚洲破处大片| 国产一区二区主播在线| 欧美经典一区二区三区| 欧美在线观看天堂一区二区三区| 国内成+人亚洲+欧美+综合在线| 久久伊人精品天天| 亚洲视频一区二区| 久久精品女人的天堂av| 激情欧美日韩一区| 欧美日韩一区二区国产| 欧美一区二区视频在线观看2020| 国内精品久久久| 国产精品福利在线观看网址| 久久裸体视频| 亚洲视频每日更新| 亚洲美洲欧洲综合国产一区| 国产精品麻豆va在线播放| 久久―日本道色综合久久| 一本久久综合| 日韩亚洲精品视频| 亚洲国产一区在线| 精品成人一区| 国产一区二区在线免费观看| 欧美日韩国产探花| 性感少妇一区| 午夜精品久久久久久久白皮肤| 99www免费人成精品| 亚洲精品免费一二三区| 尤物视频一区二区| 在线免费精品视频| 国产欧美日韩一区二区三区在线观看 | 韩国一区电影| 国产一区二区| 国产精品一卡二卡| 国产欧美日韩视频| 国产一区 二区 三区一级| 狠狠噜噜久久| 亚洲国产精品成人一区二区| 亚洲电影在线看| 亚洲激情av| 亚洲精品资源| 亚洲三级影院| 亚洲美女av黄| 宅男噜噜噜66一区二区 | 欧美成人日韩| 夜夜爽av福利精品导航 | 亚洲国产精品第一区二区| 在线精品国精品国产尤物884a| 国产欧美日韩免费| 一区在线观看| 亚洲激情专区| 亚洲精品美女在线观看播放| 亚洲午夜高清视频| 久久久久久**毛片大全| 一区二区三区视频观看| 翔田千里一区二区| 久久精品五月婷婷| 欧美日本一道本在线视频| 国产精品一区二区在线观看| 影音先锋日韩精品| 一区二区三区久久网| 欧美一二三区精品| 久热精品视频在线观看一区| 欧美日韩在线一区| 国产午夜精品美女毛片视频| 亚洲黄色免费网站| 午夜精品福利电影| 欧美国产日韩亚洲一区| 国产亚洲精品成人av久久ww| 亚洲激情女人| 欧美成人国产一区二区| 国产亚洲va综合人人澡精品| 亚洲六月丁香色婷婷综合久久| 欧美有码在线视频| 欧美日本国产一区| 亚洲女同同性videoxma| 欧美激情视频在线免费观看 欧美视频免费一 | 国产一区二区观看| 亚洲美女诱惑| 另类亚洲自拍| 国产自产在线视频一区| 午夜欧美视频| 欧美视频中文一区二区三区在线观看| 韩国女主播一区二区三区| 亚洲亚洲精品三区日韩精品在线视频| 欧美国产免费| 一区在线影院| 久久久久天天天天| 国产视频一区在线| 欧美伊人久久| 国产视频久久久久久久| 亚洲欧美第一页| 国产精品户外野外| 亚洲性色视频| 另类春色校园亚洲| 亚洲激情在线| 欧美精品在欧美一区二区少妇| 亚洲国产精品va在线观看黑人| 欧美一区二区三区日韩| 国产目拍亚洲精品99久久精品| 午夜久久资源| 激情综合五月天| 欧美精品激情在线观看| 一本一本久久| 国产亚洲一区精品| 鲁大师影院一区二区三区| 亚洲日韩视频| 国产精品久久久久天堂| 久久久精品免费视频|