我注意到如果你用C++在代碼中初始化一個靜態變量,初始化只在你第一次運行該函數時運行。是什麼讓一個靜態變量只初始化一次?
這很酷,但是如何實施?它是否會轉化爲某種扭曲的陳述? (如果給定的值,然後..)
void go(int x)
{
static int j = x ;
cout << ++j << endl ; // see 6, 7, 8
}
int main()
{
go(5) ;
go(5) ;
go(5) ;
}
我注意到如果你用C++在代碼中初始化一個靜態變量,初始化只在你第一次運行該函數時運行。是什麼讓一個靜態變量只初始化一次?
這很酷,但是如何實施?它是否會轉化爲某種扭曲的陳述? (如果給定的值,然後..)
void go(int x)
{
static int j = x ;
cout << ++j << endl ; // see 6, 7, 8
}
int main()
{
go(5) ;
go(5) ;
go(5) ;
}
是,它並通常翻譯成隱if
語句內部布爾標誌。因此,在最基本的實現你的宣言通常轉化爲類似
void go(int x) {
static int j;
static bool j_initialized;
if (!j_initialized) {
j = x;
j_initialized = true;
}
...
}
最重要的是,如果你的靜態對象有一個不平凡的析構函數,語言必須遵循另一條規則:這樣的靜態對象必須按照其建設的相反順序進行破壞。由於構建順序僅在運行時才知道,因此銷燬順序也在運行時定義。所以,每次用非平凡的析構函數構造一個局部靜態對象時,程序都必須將它註冊到某種線性容器中,稍後它將使用這個容器以適當的順序破壞這些對象。
不用說,實際的細節取決於實施。
值得一補充說,當涉及到「原始」類型(如在你的榜樣int
)與編譯時間常數初始化的靜態對象,編譯器是免費的,在啓動時初始化對象。你永遠不會注意到這種差異。但是,如果你把一個「非基本」對象
void go(int x) {
static std::string s = "Hello World!";
...
一個更復雜的例子,然後用if
上述做法是,你應該期望在生成的代碼中查找,即使對象與編譯初始化什麼時間常數。
在你的情況下,初始化器在編譯時不知道,這意味着編譯器必須延遲初始化並使用隱含的if
。
是的,編譯器通常會生成一個隱藏的布爾值「是否已經初始化?」標誌和每次執行該功能時都會運行的if
。
有更多的閱讀材料在這裏:How is static variable initialization implemented by the compiler?
雖然這的確是「某種扭曲,如果」扭曲可能會超過你想象的......
ZoogieZork對AndreyT的回答評論倒是一個重要的方面:初始化靜態局部變量 - 在一些編譯器包括GCC - 默認線程安全(編譯器命令行選項可以禁用它)。因此,它使用一些線程間同步機制(一種互斥體或某種原子操作),它可能是相對較慢的。如果你不習慣 - 性能明智 - 在你的函數中明確使用這樣的操作,那麼你應該考慮是否有一個對變量的惰性初始化影響較小的替代方案(即,自己以線程安全的方式明確構建它)某處只有一次)。很少有功能對性能非常敏感,所以儘管這很重要 - 不要讓它破壞你的一天,或者讓你的代碼更加複雜,除非你的程序太慢了,你的分析器會指責這個區域。
'在某些編譯器上是錯誤的:**是強制**,靜態初始化是線程安全的。請參閱:http://stackoverflow.com/q/8102125/2757035 – 2017-01-24 20:53:09
@underscore_d:這個答案是爲C++ 03編寫的,如果你想說改變了C++ 11以後的事情。 – 2017-02-16 06:45:38
它們僅初始化一次,因爲這是C++標準的要求。這如何發生完全取決於編譯器供應商。根據我的經驗,編譯器會生成並使用本地隱藏標誌。
它是如何實現_什麼編譯器_? – 2011-04-06 13:59:32