简体   繁体   English

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

[英]Is the typical C++ implementation of Factory class flawed?

I have the need to implement factory class in C++, but when I was thinking about that, I found one big problem that I couldn't solve, and I found out, that all factory implementation examples around are flawed in the same way. 我需要在C ++中实现工厂类,但是当我考虑这个时,我发现了一个我无法解决的大问题,并且我发现,所有工厂实现示例都以同样的方式存在缺陷。 I'm probably the one who is wrong, but please tell me why. 我可能是那个错了,但请告诉我原因。

So here is simple "typical" factory implementation, it allows me to register new objects without changing the Factory class. 所以这里是简单的“典型”工厂实现,它允许我在不更改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;
}

Ok, this code looks fancy and it works, but here comes the but: 好吧,这段代码看起来很花哨而且很有效,但是接下来是:

The C++ standard doesn't allow me to define the order in which global (static) variables will be defined. C ++标准不允许我定义全局(静态)变量的定义顺序。

I have 3 static variables here 我这里有3个静态变量

Apple::registered;
Banana::registered;
Factory::factory;

The Factory::factory pointer needs to be defined to NULL before the Apple(or Banana)::registered variable, or the Factory::instance method will work with uninitialized value, and behave unpredictably. Factory::factory指针需要在Apple(或Banana):: registered变量之前定义为NULL,否则Factory::instance方法将使用未初始化的值,并且行为不可预测。

So, what am I not getting here? 那么,我到底在哪里? Is the code really working only by an accident? 代码真的只是偶然发生了吗? If so, how should I solve the issue? 如果是这样,我该如何解决这个问题呢?

All global POD data is guaranteed to be initialized to a constant value before any initializers run. 在任何初始化程序运行之前,所有全局POD数据都保证初始化为常量值。

So at the start of your program, before any of the register calls are made and before main is run, the pointer is NULL and all of the bools are false, automatically. 因此,在程序开始时,在进行任何寄存器调用之前和main运行之前,指针为NULL并且所有bool都自动为假。 Then the initializers run, including your register calls. 然后运行初始化程序,包括您的注册调用。

Edit: Specifically, from the standard (3.6.2.2: Initialization of non-local objects): 编辑:具体来说,从标准(3.6.2.2:非本地对象的初始化):

Together, zero-initialization and constant initialization are called static initialization; 零初始化和常量初始化一起称为静态初始化; all other initialization is dynamic initialization. 所有其他初始化是动态初始化。 Static initialization shall be performed before any dynamic initialization takes place. 在进行任何动态初始化之前,应执行静态初始化。

All static variables are initialized before the program begins to run. 在程序开始运行之前初始化所有静态变量。 They are set at compile time and baked right into the executable. 它们在编译时设置并烘焙到可执行文件中。

The only issue arises when one static variable depends on another: 当一个静态变量依赖于另一个时,唯一的问题就出现了:

In a.hpp: 在a.hpp中:

static int a = 1;

in b.hpp: 在b.hpp中:

extern int a;
static int b = a;

The order in which static variables are initialized is not well defined, so b may or may not be 1 in this example. 初始化静态变量的顺序没有很好地定义,因此在本例中b可能是也可能不是1。 As long as your variables don't depend on each other, you're fine. 只要你的变量不相互依赖,你就没事了。 Furthermore, is you don't give an initial value, static members are set to zero by default. 此外,如果不提供初始值,则默认情况下静态成员设置为零。

I've tended to see the 'instance' method of Factory implemented as follows: 我倾向于看到Factory的'instance'方法实现如下:

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

However, the point is that all access to the instance runs through the static instance method. 但是,重点是对实例的所有访问都通过静态实例方法运行。 The calls to register the two fruit classes for example use Factory::instance() to obtain the singleton which will guarantee that the initializer for Factory::factory has executed. 注册两个水果类的调用例如使用Factory :: instance()来获取单例,这将保证Factory :: factory的初始化程序已执行。 In my posted alternative implementation the static initialization only occurs the first time the method is called. 在我发布的替代实现中,静态初始化仅在第一次调用方法时发生。

The possible issues with Apple::registered and Banana::registered depend on where they might be used from. Apple :: registered和Banana :: registered的可能问题取决于它们的使用位置。 In the posted code they aren't used at all. 在发布的代码中,根本不使用它们。 If used only within apple.cpp and banana.cpp respectively then there is no issue with order of initialization. 如果仅分别在apple.cpp和banana.cpp中使用,则初始化顺序没有问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM