我通常会做类似的事情:
class CarFactory
{
public:
static void RegisterCar(CarType t, std::function<Car*()> f)
{
getMap().emplace(t, f);
}
static Car* create(CarType type)
{
return getMap().at(type)();
}
private:
static std::unorderd_map<CarType, std::function<Car*()> >& getMap()
{
static std::unorderd_map<CarType, std::function<Car*()> > m;
return m;
}
};
并在每个类的实现中:
class BMWCar : public Car
{
struct Init
{
Init()
{
CarFactory::RegisterCar(BMW, [](){return new BMWCar(); });
}
};
static Init initializeBmwCar;
/** .. */
};
/*** BMWCar.cpp ***/
BMWCar::Init BMWCar::initializeBmwCar;
这是可行的,因为每种类型在静态初始化期间使用以下方法初始化自己的工厂:static Init
object.
这段代码中的巨大痛苦是为了避免初始化顺序失败:一个简单的实现将简单地使用静态映射CarFactory
。不幸的是,不能保证BMWCar::initializeBmwCar
构造函数将运行after地图在CarFactory
。有时某些编译器可能会工作,有时可能会崩溃。
所以想法是使用静态函数(getMap
)与静态变量(m
),保证第一次初始化getMap
叫做。
我知道clang
/llvm
使用此模式来注册优化过程。
另一种更复杂但更灵活的解决方案是设计一个插件系统,其中每个 DLL 都实现一个car
输入并导出一个CreateCar
功能。
然后你就可以收集所有这些CreateCar
在初始化期间通过动态加载库并调用GetProcAddress
/dlsym
.
这两种解决方案在 Windows 上实现起来都可能很棘手,因为(除非Car
是抽象的)基础Car
实现需要进入其自己的库,并且每个插件 dll 都需要与该库链接。