4

我需要在 C++ 中实现工厂类,但是当我想到这个时,我发现了一个我无法解决的大问题,我发现周围所有的工厂实现示例都存在相同的缺陷。我可能是错的人,但请告诉我为什么。

所以这里是简单的“典型”工厂实现,它允许我在不更改工厂类的情况下注册新对象。

//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;

Factory::factory指针需要在 Apple(或 Banana)::registered 变量之前定义为 NULL ,否则该方法Factory::instance将使用未初始化的值,并且行为不可预测。

那么,我没有得到什么?代码真的只是偶然地工作吗?如果是这样,我应该如何解决这个问题?

4

3 回答 3

11

保证在任何初始化程序运行之前将所有全局 POD 数据初始化为一个常量值。

因此,在您的程序开始时,在进行任何寄存器调用之前以及在 main 运行之前,指针是 NULL 并且所有布尔值都自动为假。然后初始化程序运行,包括您的注册调用。

编辑:具体来说,来自标准(3.6.2.2:非本地对象的初始化):

零初始化和常量初始化合称为静态初始化;所有其他初始化都是动态初始化。静态初始化应在任何动态初始化发生之前执行。

于 2010-10-17T02:24:53.910 回答
4

所有静态变量都在程序开始运行之前初始化。它们在编译时设置并直接烘焙到可执行文件中。

当一个静态变量依赖于另一个静态变量时,就会出现唯一的问题:

在 a.hpp 中:

static int a = 1;

在 b.hpp 中:

extern int a;
static int b = a;

静态变量的初始化顺序没有很好的定义,所以在这个例子中 b 可能是也可能不是 1。只要您的变量不相互依赖,就可以了。此外,如果您不提供初始值,静态成员默认设置为零。

于 2010-10-17T02:27:12.223 回答
2

我倾向于看到工厂的“实例”方法实现如下:

static Factory& instance()
{
    static Factory *factory = new Factory();
    return *factory;
}

但是,关键是对实例的所有访问都是通过静态实例方法运行的。例如,注册两个水果类的调用使用 Factory::instance() 来获取单例,这将保证 Factory::factory 的初始化程序已执行。在我发布的替代实现中,静态初始化仅在第一次调用该方法时发生。

Apple::registered 和 Banana::registered 可能存在的问题取决于它们的使用位置。在发布的代码中,它们根本没有被使用。如果分别仅在 apple.cpp 和banana.cpp 中使用,则初始化顺序没有问题。

于 2010-10-17T02:47:20.703 回答