2010-05-17 58 views
3

爲什麼下面的代碼引發異常(在createObjects來電map::at) alternativly代碼(及其輸出)可以被看作here(靜態初始化/模板實例化)問題與工廠模式

intererestingly的代碼工作預計如果註釋行不用微軟和gcc編譯器註釋(見here),甚至可以使用initMap作爲普通靜態變量而不是靜態獲取器。

爲了這個,我能想到的唯一原因是,靜態registerHelper_對象(factory_helper_)和std::map對象(initMap)是錯誤的初始化命令,但是我看不到怎麼會發生,因爲地圖對象是建立在第一次使用和那在factory_helper_構造函數,所以一切都應該沒問題呢? 我更加驚訝的是,那些沒有()行修復了這個問題,因爲那個調用doNothing()會在臨界區(當前失敗)之後發生。

編輯:調試顯示,沒有調用factory_helper_.doNothing(),factory_helper_的構造函數永遠不會被調用。

#include <iostream> 
#include <string> 
#include <map> 

#define FACTORY_CLASS(classtype) \ 
extern const char classtype##_name_[] = #classtype; \ 
class classtype : FactoryBase<classtype,classtype##_name_> 

namespace detail_ 
{ 
    class registerHelperBase 
    { 
    public: 
     registerHelperBase(){} 
    protected: 
     static std::map<std::string, void * (*)(void)>& getInitMap() { 
      static std::map<std::string, void * (*)(void)>* initMap = 0; 
      if(!initMap) 
       initMap= new std::map<std::string, void * (*)(void)>(); 
      return *initMap; 
     } 
    }; 

    template<class TParent, const char* ClassName> 
    class registerHelper_ : registerHelperBase { 
     static registerHelper_ help_; 
    public: 
     //void doNothing(){} 
     registerHelper_(){ 
      getInitMap()[std::string(ClassName)]=&TParent::factory_init_; 
     } 
    }; 
    template<class TParent, const char* ClassName> 
    registerHelper_<TParent,ClassName> registerHelper_<TParent,ClassName>::help_; 
} 

class Factory : detail_::registerHelperBase 
{ 
private: 
    Factory(); 
public: 
    static void* createObject(const std::string& objclassname) { 
     return getInitMap().at(objclassname)(); 
    } 
}; 


template <class TClass, const char* ClassName> 
class FactoryBase { 
    private: 
     static detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> factory_helper_; 
     static void* factory_init_(){ return new TClass();} 
    public: 
     friend class detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName>; 
     FactoryBase(){ 
      //factory_helper_.doNothing(); 
     } 
     virtual ~FactoryBase(){}; 
}; 

template <class TClass, const char* ClassName> 
detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> FactoryBase<TClass,ClassName>::factory_helper_; 


FACTORY_CLASS(Test) { 
public: 
    Test(){} 
}; 

int main(int argc, char** argv) { 
    try { 
     Test* test = (Test*) Factory::createObject("Test"); 
    } 
    catch(const std::exception& ex) { 
     std::cerr << "caught std::exception: "<< ex.what() << std::endl; 
    } 
    #ifdef _MSC_VER 
     system("pause"); 
    #endif 
    return 0; 
} 
+0

將一些調試打印添加到您的構造函數和訪問函數中,查看哪些對象正在創建以及何時。 – 2010-05-17 19:48:38

回答

7

該問題與初始化順序無關,而是與模板實例化有關。

模板化代碼是根據需要實例化的,也就是說,編譯器不會實例化任何未在程序中使用的模板化代碼。特別是,在你的情況下,靜態類成員FactoryBase<>::factory_helper_沒有被實例化,因此它不存在於最終的二進制文件中,它不會自己註冊...(你可以用gnu工具鏈中的'nm'來檢查它,這會顯示出現在你的可執行文件的符號列表)

嘗試改變FactoryBase構造這樣:

template <class TClass, const char* ClassName> 
class FactoryBase { 
    //... 
    FactoryBase(){ 
     factory_helper_; 
    } 
    //... 
}; 

這將迫使編譯成實際實例二進制的靜態成員,你應該設置。沒有必要創建一個空方法並調用它。

EDIT:作爲答案的評論,對段的端§14.7.1[temp.inst]/1在當前標準

除非類的一個成員模板或 成員模板已經明確 實例化或明確 專業化,當 的專業化要求的部件 上下文中引用的 成員的專業化隱式實例 定義存在;特別是,除非靜態數據 構件處於方式使用本身 需要靜態 數據成員的定義存在不發生 的靜態數據成員 的初始化(和任何相關聯的 副作用)。

+0

我認爲模板類的靜態成員會在它們的封閉類被實例化時自動實例化嗎? – smerlin 2010-05-17 21:12:31

+0

爲什麼在FactoryBase中沒有'void doNothing(){factory_helper _;}'函數導致'factory_helper_'的定義(試過了 - 它沒有)?是不是需要定義'factory_helper_'並且應該引起它的實例化? – smerlin 2010-05-18 12:56:54

+0

如果你正在調用'doNothing',那麼這是一個需要靜態成員的情況,但是如果你不從代碼中的任何地方調用它,那麼'doNothing'成員函數將不會被實例化,因此它不會觸發實例化'factory_helper_'靜態成員屬性。 – 2010-05-18 14:20:00

0
#define FACTORY_CLASS(classtype) \ 
class classtype; \ 
extern const char classtype##_name_[] = #classtype; \ 
template detail_::registerHelper_<FactoryBase<classtype,classtype##_name_>,classtype##_name_> FactoryBase<classtype,classtype##_name_>::factory_helper_; \ 
class classtype : FactoryBase<classtype,classtype##_name_> 

明確實例factory_helper_固定的問題。