我试图在加载时向工厂注册一堆类。我的策略是利用静态初始化来确保在 main() 开始之前,工厂已准备就绪。当我动态链接我的库时,这种策略似乎有效,但当我静态链接时无效;当我静态链接时,只有我的一些静态数据成员被初始化。
假设我的工厂生产汽车。我有 CarCreator 类可以实例化少数汽车,但不是全部。我希望工厂收集所有这些 CarCreator 类,以便寻找新 Car 的代码可以去工厂,而不必知道谁将进行实际构建。
所以我有
汽车类型.hpp
enum CarTypes
{
prius = 0,
miata,
hooptie,
n_car_types
};
我的工厂.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;
};
我的工厂.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::registered 将被初始化,静态链接我的库,它不会被初始化。
对于静态构建,当有人去工厂请求 Miata 时,car_creator_map
的 miata 元素将指向 NULL,程序将退出。
私有(private)静态整数数据成员有什么特别之处,它们的初始化会以某种方式被跳过吗?是否仅在使用该类时才初始化静态数据成员?我的 CarCreator 类没有在任何头文件中声明;它们完全存在于 .cpp 文件中。编译器是否可能正在内联初始化函数并以某种方式避免调用 MyFactory::factory_register
?
这个注册问题有更好的解决方案吗?
在单个函数中列出所有 CarCreators,向工厂显式注册每个 CarCreators,然后保证函数被调用,这不是一种选择。特别是,我想将几个库链接在一起,并在这些单独的库中定义 CarCreator,但仍使用单一工厂来构造它们。
...
以下是一些我期待但没有解决我的问题的回复:
1) 你的单例工厂不是线程安全的。 a) 没关系,我只使用一个线程。
2) 当你的 CarCreators 被初始化时,你的单例工厂可能未被初始化(即你有一个静态初始化失败)
a) 我通过将单例实例放入函数中来使用单例类的安全版本。如果这是一个问题,如果我向 MiataCreator's::register_with_factory
方法添加打印语句,我应该会看到输出:我没有。
最佳答案
我认为您有一个静态初始化顺序失败,但工厂没有。
并不是注册标志没有被初始化,只是没有足够快地被初始化。
您不能依赖静态初始化顺序,除非:
- 在同一翻译单元(.cpp 文件)中定义的静态变量将按照列出的顺序进行初始化
- 翻译单元中定义的静态变量将在首次调用该翻译单元中的任何函数或方法之前进行初始化。
您不能依赖的是,在首次调用其他翻译单元中的函数或方法之前初始化静态变量。
特别是,您不能依赖 MiataCreator::registered(在 Miata.cpp 中定义)在 MyFactory::create_car(在 MyFactory.cpp 中定义)首次调用之前进行初始化。
像所有未定义的行为一样,有时你会得到你想要的,有时你不会,最奇怪的最看似无关的事情(例如静态链接与动态链接)可以改变它是否按照你想要的方式工作还是不是。
您需要做的是为 Miata.cpp 中定义的注册标志创建静态访问器方法,并让 MyFactory 工厂通过此访问器获取值。由于访问器与变量定义在同一个翻译单元中,因此变量将在访问器运行时初始化。然后您需要从某个地方调用此访问器。
关于c++ - 为什么静态数据成员可能没有被初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1300836/