我想在加載時註冊一堆類的工廠。我的策略是利用靜態初始化來確保在main()開始之前,工廠已經準備好了。當我動態地鏈接我的庫時,這個策略似乎工作,但是當我靜態鏈接時,這種策略不起作用;當我靜態鏈接時,只有一些靜態數據成員被初始化。爲什麼靜態數據成員不能被初始化?
假設我的工廠生產汽車。我有CarCreator類可以實例化少數汽車,但不是全部。我希望工廠能夠收集所有這些CarCreator類,以便尋找新車的代碼無需知道誰將參與實際施工就可以去工廠。
所以我有
CarTypes.hpp
enum CarTypes
{
prius = 0,
miata,
hooptie,
n_car_types
};
MyFactory.hpp
class CarCreator
{
public:
virtual Car * create_a_car(CarType) = 0;
virtual std::list<CarTypes> list_cars_I_create() = 0;
};
class MyFactory // makes cars
{
public:
Car * create_car(CarType type);
void factory_register(CarCreator *)
static MyFactory * get_instance(); // singleton
private:
MyFactory();
std::vector< CarCreator * > car_creator_map;
};
MyFactory.cpp
MyFactory:: MyFactory() : car_creator_map(n_car_types);
MyFactory * MyFactory::get_instance() {
static MyFactory * instance(0); /// Safe singleton
if (instance == 0) {
instance = new MyFactory;
}
return instance;
}
void MyFactory::factory_register(CarCreator * creator)
{
std::list<CarTypes> types = creator->list_cars_I_create();
for (std::list<CarTypes>::const_iteator iter = types.begin();
iter != types.end(); ++iter) {
car_creator_map[ *iter ] = creator;
}
}
Car * MyFactory::create_car(CarType type)
{
if (car_creator_map[ type ] == 0) { // SERIOUS ERROR!
exit();
}
return car_creator_map[ type ]->create_a_car(type);
}
...
然後,我將有具體的汽車和汽車專用創:
Miata.cpp
class Miata : public Car {...};
class MiataCreator : public CarCreator {
public:
virtual Car * create_a_car(CarType);
virtual std::list<CarTypes> list_cars_I_create();
private:
static bool register_with_factory();
static bool registered;
};
bool MiataCreator::register_with_factory()
{
MyFactory::get_instance()->factory_register(new MiataCreator);
return true;
}
bool MiataCreator::registered(MiataCreator::register_with_factory());
...
重申:動態鏈接庫我,MiataCreator ::註冊將得到初始化,靜態鏈接我的庫,它不會被初始化。
使用靜態構建,當有人去工廠請求Miata時,car_creator_map
的miata元素將指向NULL,程序將退出。
有什麼特別的私人靜態積分數據成員,他們的初始化將以某種方式跳過?靜態數據成員是否僅在使用類時才初始化?我的CarCreator類不在任何頭文件中聲明;他們完全生活在.cpp文件中。是否有可能編譯器內聯初始化函數,並以某種方式避免調用MyFactory :: factory_register
?
這個註冊問題有更好的解決方案嗎?
它不是列出IALL的CarCreators在一個單一的功能,與工廠直接註冊各一臺,然後以保證函數被調用的選項。特別是,我想將幾個庫鏈接在一起,並在這些單獨的庫中定義CarCreators,但仍然使用單個工廠來構建它們。
...
這裏有一些迴應,我期待,但它並沒有解決我的問題:
1)你的單身廠是不是線程安全的。 a)應該不重要,我只用一個線程工作。
2)你的單身廠可能當你CarCreators被初始化未被初始化(即你有一個靜態初始化失敗) a)通過將單例實例放入一個函數中,我使用了單例類的安全版本。如果這是一個問題,我應該看到輸出,如果我添加打印語句MiataCreator's::register_with_factory
方法:我不知道。
另請參閱此併發問題http://stackoverflow.com/questions/1300778/how-to-prevent-the-linker-from-optimizing-away-startup-code – 2009-08-19 15:55:40
我會使用objdump和/或nm(或Windows上的dumpbin)在最終的可執行文件上。有可能代碼甚至沒有包含,因爲鏈接器看到你的程序沒有引用靜態庫中的任何東西。 – nos 2009-08-19 16:01:55
這與靜態或動態鏈接以及靜態成員初始化*順序未確定的事實有很大關係。泰勒麥克亨利的回答是對的。 – quark 2009-08-19 16:35:47