2011-11-08 125 views
5

我想通過註冊派生類的函數指針到工廠在靜態映射(工廠的成員)和創建對象的工廠模式實現查找地圖。但是我在這樣做時遇到了分段錯誤。從靜態成員函數訪問靜態映射 - 分段錯誤 - C++

代碼段:

factory.cpp

typedef Shape* (*Funcptr)(); 

std::map<int,Funcptr> Factory::funcmap; 

int Factory::registerCreator(int ShapeID, Shape *(*CFuncptr)()) { 
    Factory::funcmap[ShapeID] = CFuncptr; 
return 1; 
} 

Shape* Factory::CreateObject(int ShapeID) { 
    std::map<int,Funcptr>::iterator iter; 
    iter = funcmap.find(ShapeID); 
    if(iter != funcmap.end()){ 
     return iter->second(); 
    } 
    return NULL; 
} 

factory.h

class Factory { 
public: 
    Factory(); 
    virtual ~Factory(); 
    static int registerCreator(int, Shape *(*CFuncptr)()); 
    Shape* CreateObject(int); 
private: 
    static std::map<int,Funcptr> funcmap; 
}; 

Square.cpp

static Shape *SquareCreator() { 
    return new Square; 
} 
static int SquareAutoRegHook = Factory::registerCreator(1,SquareCreator); 

在主創建對象工廠文件分段發生故障。 你可以請建議,如果我做錯了什麼。我正在使用CppUTest進行TDD,不知道如何調試。

+0

粗略地講,不同的.cpp文件顯示爲'Funcptr' typedef的。你可以重寫你的代碼,以便在任何地方使用typedef(和測試)嗎?另外,爲什麼'SquareCreator()'聲明'靜態'? –

+0

@KerrekSB:依然沒有變化。 – Saaras

+0

無關緊要的是,如果函數指針總是兼容的話,我現在還不確定。我只是試圖自己測試這個,而我剛剛得到了一個「無法轉換」的錯誤。 –

回答

7

無法保證靜態對象的創建順序,這些靜態對象在不同的​​翻譯版本中定義,因此您有50/50鏡頭,哪一個會先發生,Factory::funcmapFactory::registerCreator(1,SquareCreator)的初始化和未定義的行爲Russian Roulette是不是一個好玩的遊戲。

解決這個問題的一種常用方法,以及the third edition of Scott Meyer's Effective C++第4項中描述的方法是使用本地靜態對象而不是全局靜態對象。在這種情況下,它意味着改變Factory看起來像這樣:

class Factory { 
public: 
    Factory(); 
    virtual ~Factory(); 
    static int registerCreator(int, Shape *(*CFuncptr)()); 
    Shape* CreateObject(int); 
private: 
    static std::map<int,Funcptr> & GetFactoryMap() { 
     static std::map<int,Funcptr> funcmap; 
     return funcmap; 
    } 
}; 

,改變Factory::registerCreator這樣:

int Factory::registerCreator(int ShapeID, Shape *(*CFuncptr)()) { 
    GetFactoryMap()[ShapeID] = CFuncptr; 
    return 1; 
} 

這樣funcmap將被初始化首次registerCreator被調用,絕不會被用來初始化。

*或者,如果你不familar與術語翻譯單元

+0

感謝您的回答。 – Saaras

+0

我已經嘗試過這個技巧,並且遇到了一些編譯器的問題,如果您在不同的靜態鏈接庫和可執行文件中使用工廠,則會創建多個funcmaps。現在我對每個庫或可執行文件都有一個funcmap,但只有在某些系統上進行了編譯。 – P1r4nh4

0

看起來像static initialization order fiasco。看起來在SquareAutoRegHook初始化時,funcmap尚未創建。

+0

如何確保初始化的順序或任何其他乾淨的方式? – Saaras

+0

這看起來是正確的。奇怪的是,如果全局需要指向它的指針,則SquareCreator()具有內部鏈接。 –

+0

@Saaras - 你不能確保靜態對象初始化的順序,所以使用初始化例程,當從'main'調用時,這些初始化例程將爲你做設置。一旦'main'啓動,你可以依靠所有的靜態變量來初始化(以一個未定義的順序)。 – littleadv