我需要在C ++中实现工厂类,但是当我考虑这个时,我发现了一个我无法解决的大问题,我发现,所有工厂实现示例都存在缺陷办法。我可能是那个错了,但请告诉我原因。
所以这里是简单的“典型”工厂实现,它允许我在不更改Factory类的情况下注册新对象。
//fruit.h
class Fruit
{
protected :
int count;
public :
Fruit(int count) : count(count) {}
virtual void show() = 0;
};
// factory.h
/** singleton factory */
class Factory
{
typedef Fruit* (*FruitCreateFunction)(int);
static Factory* factory;
std::map<std::string, FruitCreateFunction> registeredFruits;
public :
static Factory& instance()
{
if (factory == NULL)
factory = new Factory();
return *factory;
}
bool registerFruit(const std::string& name, Fruit* (createFunction)(int))
{
registeredFruits.insert(std::make_pair(name, createFunction));
return true;
}
Fruit* createFruit(const std::string& name, int count)
{
return registeredFruits[name](count);
}
};
//factory.cpp
Factory* Factory::factory = NULL;
//apple.h
class Apple : public Fruit
{
static Fruit* create(int count) { return new Apple(count); }
Apple(int count) : Fruit(count) {}
virtual void show() { printf("%d nice apples\n", count); };
static bool registered;
};
// apple.cpp
bool Apple::registered = Factory::instance().registerFruit("apple", Apple::create);
//banana.h
class Banana : public Fruit
{
static Fruit* create(int count) { return new Banana(count); }
Banana(int count) : Fruit(count) {}
virtual void show() { printf("%d nice bananas\n", count); };
static bool registered;
};
// banana.cpp
bool Banana::registered = Factory::instance().registerFruit("banana", Banana::create);
// main.cpp
int main(void)
{
std::vector<Fruit*> fruits;
fruits.push_back(Factory::instance().createFruit("apple", 10));
fruits.push_back(Factory::instance().createFruit("banana", 7));
fruits.push_back(Factory::instance().createFruit("apple", 6));
for (size_t i = 0; i < fruits.size(); i++)
{
fruits[i]->show();
delete fruits[i];
}
return 0;
}
好的,这段代码看起来很花哨而且很有效,但是接下来是:
C ++标准不允许我定义全局(静态)变量的定义顺序。
我这里有3个静态变量
Apple::registered;
Banana::registered;
Factory::factory;
需要在 之前将<{1}}指针定义为NULL Apple(或Banana)::注册变量或Factory::factory
方法将使用未初始化的值,并且行为不可预测。
那么,我到底在哪里?代码真的只是偶然发生了吗?如果是这样,我该如何解决这个问题?
答案 0 :(得分:11)
保证所有全局POD数据在任何初始化程序运行之前初始化为常量值。
因此,在程序开始时,在进行任何寄存器调用之前和main运行之前,指针为NULL并且所有bool都自动为假。然后运行初始化程序,包括您的注册调用。
编辑:具体来说,从标准(3.6.2.2:非本地对象的初始化):
一起,零初始化和 调用常量初始化 静态初始化;所有其他 初始化是动态的 初始化。静态初始化 应在任何动态之前执行 初始化发生。
答案 1 :(得分:4)
在程序开始运行之前初始化所有静态变量。它们在编译时设置并烘焙到可执行文件中。
当一个静态变量依赖于另一个静态变量时,唯一的问题就出现了:
在a.hpp中:
static int a = 1;
b.hpp中的:
extern int a;
static int b = a;
初始化静态变量的顺序没有很好地定义,因此在本例中b可能是也可能不是1。只要你的变量不相互依赖,你就没事了。此外,如果您没有给出初始值,默认情况下静态成员将设置为零。
答案 2 :(得分:2)
我倾向于看到Factory的'instance'方法实现如下:
static Factory& instance()
{
static Factory *factory = new Factory();
return *factory;
}
但是,重点是对实例的所有访问都通过静态实例方法运行。注册两个水果类的调用例如使用Factory :: instance()来获取单例,这将保证Factory :: factory的初始化程序已执行。在我发布的替代实现中,静态初始化仅在第一次调用方法时发生。
Apple :: registered和Banana :: registered的可能问题取决于它们的使用位置。在发布的代码中,根本不使用它们。如果仅在apple.cpp和banana.cpp中使用,则初始化顺序没有问题。