2011-11-01 232 views
2

我有一個類,它包含一個靜態成員,一個字符串映射到函數指針。該映射旨在用一組靜態映射填充一次,並且不會隨後進行修改。強制靜態成員初始化

我的問題是,如何確保地圖在初始化之前不被訪問?我的代碼目前看起來是這樣的:

class MyClass 
{ 
    static MapType s_myMap; 
public: 
    static const MapType& getTheMap() 
    { 
    if (s_myMap.empty()) 
    { 
     // Populate the map 
    } 
    return s_myMap; 
    } 
}; 

,工作正常進行的MyClass外部客戶,但並不妨礙內部類成員直接訪問private地圖已經初始化之前。

爲了解決這個問題,我想製作地圖本地的getter方法的:

class MyClass 
{ 
public: 
    static const MapType& getTheMap() 
    { 
    static MapType s_myMap; 
    if (s_myMap.empty()) 
    { 
     // Populate the map 
    } 
    return s_myMap; 
    } 
}; 

這是一個好主意,或者是有實現這一目標的一個更好的辦法?

+0

將靜態移動到函數是一個好主意。只要沒有其他的靜態對象初始化調用'getTheMap',你就沒事。 –

+0

地圖是否動態初始化?如果沒有,你可以將它填充到初始化程序中(假設你有最近的編譯器)。 –

+0

@KerrekSB它是靜態初始化的。爲了檢查我已經理解了你,你是否建議將'//填充地圖'所隱含的代碼移動到一個單獨的初始化方法(例如'initialiseMap()'),然後通過'MapType MyClass :: s_myMap = initialiseMap()'? – atkins

回答

1

將靜態函數移入該函數將解決初始化問題的任何順序 ,但它可能會使您的訂單損壞一個,即 。在許多情況下,最好使用指針和動態分配,以便映射永遠不會被破壞。

至於初始化它,我經常使用兩個迭代器的構造函數,所以我可以使這個映射本身爲const。要做到這一點,只是定義 struct用轉換操作符,是這樣的:

struct MapInitData 
{ 
    char const* key;  // Or whatever type is needed. 
    char const* value; // Or whatever type is needed. 
    operator MapType::value_type() const 
    { 
     return MapType::value_type(key, value); 
    } 
}; 

MapInitData const mapInitTable[] = 
{ 
    { "key1", "value1" }, 
    // ... 
}; 

MapType const ourMap(begin(mapInitTable), end(mapInitTable)); 
+0

這真是太棒了 - 我真的想把地圖製作成'const',但是我不認爲我能夠做到。 – atkins

+0

我討厭這個建議(關於指針和動態分配)。銷燬問題的順序很容易解決。 http://stackoverflow.com/questions/335369/finding-c-static-initialization-order-problems/335746#335746。 –

+0

@LokiAstari我沒有看到那裏的代碼解決任何問題。銷燬順序僅在特定的對象子集內排序,如靜態聲明的那些。假設你有一個'static std :: auto_ptr ',並且在它的構造很久之後,你重置它以指向一個在它的析構函數中使用這個靜態對象的對象。 (這種情況並不常見---至少有希望,但這是可能的) –

1

您可以爲此集合聲明一個類並將其填充到構造函數中。

+0

是的,這也可以 - 謝謝。 – atkins

1

如果在全局/ namespace範圍內沒有調用MyClass::getTheMap(),那麼在初始化之前您不必擔心使用的數據成員static

不過,如果上述static方法getTheMap()在全球/ namespace範圍使用:

SomeGlobal object = MyClass::getTheMap(); 

那麼你目前的做法似乎是罰款。

+0

我對「initialise」一詞有點寬鬆。我的意思是,在訪問之前,地圖應該填入一組條目。數據成員使用'MapType MyClass :: s_myMap = MapType()',以更精確的方式初始化爲空映射。 – atkins

+0

@atkins:如果你還沒有首先陷入未定義的行爲,那麼getTheMap()會正確地設置條目。問題在於,使用全局靜態變量很容易陷入未定義的行爲。 –

0

你的函數「getTheMap」應該有一個線程安全中有一個鎖。將其移到本地功能是一個好主意。

我認爲這與drdobbs文章「C++和雙重檢查鎖定的危險」http://drdobbs.com/cpp/184405726類似於這個問題。在這裏他通過使用案例和不同類型的單身人士安全其他模式與threadsafety。

至於破壞,你需要摧毀它嗎?