2010-03-03 66 views
6

我在很久沒有回到C++了,而且我對於我對相當熟知的靜態初始化問題的理解有點磕磕絆絆。C++靜態常量和初始化(有沒有一個失敗)

比方說,我有一個簡單的類Vector2下面給出(請注意,我知道,X和Y應該是私人的getter和setter方法,這些都只是爲了簡潔,省略):

class Vector2 { 

public: 
    Vector2(float x, float y) :x(x), y(y) {}; 
    float x,y; 
} 

現在,如果我想指定一個靜態const成員來表示一個Vector2,其中x和y設置爲1,我不確定如何繼續 - 靜態常量成員是否會陷入靜態初始化問題或將它們設置爲常量均值的行爲他們都還好?我有以下幾種可能醞釀:

可能性1:

// .h 
class Vector2 { 

public: 
    Vector2(float x, float y) :x(x), y(y) {} 
    static const Vector2 ONE; 
    float x,y; 
}; 

// .cpp 
const Vector2 Vector2::ONE = Vector2(1.f, 1.f); 

可能性2:

// .h 
class Vector2 { 

public: 
    Vector2(float x, float y) :x(x), y(y) {} 
    static const Vector2& getOne(); 
    float x,y; 
private: 
    static const Vector2 ONE; 
}; 

// .cpp 
const Vector2 Vector2::ONE = Vector2(1.f, 1.f); 

static const Vector2& Vector2::getOne() { 
    return ONE; 
} 

可能性3:

// .h 
class Vector2 { 

public: 
    Vector2(float x, float y) :x(x), y(y) {} 
    static const Vector2& getOne(); 
    float x,y; 
}; 

// .cpp 
const Vector2& Vector2::getOne() { 
    static Vector2 one(1.f,1.f); 
    return one; 
} 

現在,我的首選方式來寫這個就像在可能性2中一樣,只是因爲它對我來說是一種更舒適的語法。但是,如果我從另一個類的另一個靜態方法調用getOne()方法,我會冒險崩潰和燃燒?正如我所說,這是因爲我使用的是靜態常量而不是普通靜態,因此我提出了這個問題,因爲我在純靜態類成員問題上發現了很多,但對常量靜態問題沒有任何幫助。

我懷疑我沒有得到任何東西,因爲我使用的是靜態常量,並且需要與Possibility 3一起才能安全,但我只是想問一下,如果有人能爲我解釋這一點。

我意識到我可能會打開自己的鏈接指向我正在問什麼,但我看過,發現之前沒有找到。

任何幫助將不勝感激。

+0

從你的問題來看,這並不完全清楚,但如果你試圖從另一個靜態初始化方法*直接訪問靜態成員,那麼你只有**傾向於失敗。在調用main()之後,從另一個函數訪問它是否爲static – sbk

+0

感謝您的評論sbk。我的問題應該更清楚一些。我的目的是爲了確保靜態成員可以被其他類中的靜態初始化器在另一個cpp文件中使用。雖然我的其他類目前都沒有專門使用Vector2 :: getOne()來初始化另一個靜態成員,但確實存在一些靜態初始化器的鏈。我選擇Vector2作爲一個簡單的例子,所以我可以從這裏的評論中理解在整個項目中推出的最佳模式。非常感謝評論。 –

回答

10

所有這些,除了可能性3,都受到靜態初始化順序失敗的影響。這是因爲你的班級不是POD。在C++ 0x中,這個問題可以通過標記構造函數constexpr來解決,但在C++ 03中沒有這樣的解決方案。

您可以刪除構造函數來解決C++ 03的問題,並使用

const Vector2 Vector2::ONE = { 1.f, 1.f }; 

這是初始化POD初始化,並在列表中的所有初始化是常量表達式(爲靜態的目的初始化)。它們的初始化發生在任何代碼運行之前,可能在初始化之前訪問它。

3.6.2

具有靜態存儲的持續時間(3.7.1)的對象應是零初始化(8.5)的任何其它初始化發生之前。使用常量表達式進行零初始化和初始化統稱爲靜態初始化;其他所有初始化都是動態初始化在任何動態初始化發生之前,初始化具有用常量表達式(5.19)初始化的靜態存儲持續時間的POD類型對象(3.9)。

8.5.1/14

當與靜態存儲持續時間的聚集體用括號括起來的初始化列表初始化,如果所有的成員初始化表達式是常量表達式,並且集合體是一個POD類型,初始化應在初始化(3.6.2)的靜態階段完成;否則,在靜態階段或初始化的動態階段期間,具有常量表達式的成員的初始化是不確定的。

+0

感謝您的評論Johannes。我感謝您花時間回答並確認我的3種可能性,第三是唯一安全的使用方法。我沒有想過要走下POD初始化路徑,所以這是非常豐富的。雖然我有更復雜的類,POD方法不起作用(因爲不是所有的類都可以當作POD),但仍然非常方便,因爲我可以確定地看到我可以使用它的地方。非常感謝 –

+0

我剛剛意識到我從未接受過您的答案。對不起,但現在糾正,近一年後 –

1

請注意,可能性3不是線程安全的。

例如參見「C++範圍的靜態初始化不是線程安全的,故意的!」在http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx

+1

請注意,這個答案不再是正確的:線程安全現在[由標準施加](http://stackoverflow.com/questions/8102125/is-local-靜態變量初始化線程安全的,在-C11) – Arnaud