【问题标题】:Is the typical C++ implementation of Factory class flawed?Factory 类的典型 C++ 实现是否存在缺陷?
【发布时间】:2010-10-17 02:19:54
【问题描述】:

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

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

【问题讨论】:

  • Apple::registered 设置的值是否依赖于 Factory::factory?
  • 这更多是您实现单例而不是工厂的问题。
  • 模式之所以被称为模式,是因为它们不是一种实现。说有一个典型的模式是矛盾的,因为模式需要针对特定​​情况进行定制,因此它是一种模式,不是一种实现。
  • 你应该考虑修改你的代码,你正在泄漏你的单例并且没有将分配的Fruit 放在某种智能管理器中,这也会使你面临内存泄漏(在异常情况下)。
  • @Matthieu M Thx,我知道这个版本会泄露,我想让它尽可能简单。其实我在Factory::instance函数中使用局部静态变量作为工厂实例,所以根本没有用到Factory::factory指针。

标签: c++ singleton factory factory-pattern


【解决方案1】:

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

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

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

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

【讨论】:

  • 显然是 C++ 标准,3.6.2.2 左右。 (见stackoverflow.com/questions/2960307/…
  • 我希望人们不要在这里使用全局这个词。它是所有具有静态寿命的变量(略多于全局变量)。
【解决方案2】:

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

唯一的问题是当一个静态变量依赖另一个静态变量时:

在 a.hpp 中:

static int a = 1;

在 b.hpp 中:

extern int a;
static int b = a;

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

【讨论】:

  • 它在编译单元中定义得很好。当依赖关系跨越多个编译单元时,它是未定义的。
  • @sharth:感谢您添加说明。
  • 当一个文件中的代码依赖于另一个文件中定义的变量时,就会出现唯一的问题。您的示例定义明确,因为它们是按声明顺序初始化的。
  • 我已经添加了我希望答案更清晰的内容。感谢您指出问题的确切性质。
  • 不,这个例子没有很好的定义。 b 的值是否为 1 是不确定的。
【解决方案3】:

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

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

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

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

【讨论】:

  • 为什么要使用指针。只需将其声明为静态成员变量,就无需调用 new,因此您不会泄漏内存。
  • 是的。取点。只是在评论我最常看到它是如何实现的,很可能是由于parashift.com/c++-faq-lite/ctors.html#faq-10.17
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-07
  • 2019-06-03
  • 2018-12-11
  • 2012-03-06
  • 2023-03-05
  • 1970-01-01
相关资源
最近更新 更多