Factory类的典型C ++实现是否有缺陷?

时间:2010-10-17 02:19:54

标签: c++ singleton factory factory-pattern

我需要在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方法将使用未初始化的值,并且行为不可预测。

那么,我到底在哪里?代码真的只是偶然发生了吗?如果是这样,我该如何解决这个问题?

3 个答案:

答案 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中使用,则初始化顺序没有问题。