是典型的C++工厂类的实现有缺陷吗?

发布于 2024-09-27 18:17:34 字数 2408 浏览 5 评论 0原文

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

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

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. 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.

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

I have 3 static variables here

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.

So, what am I not getting here? Is the code really working only by an accident? If so, how should I solve the issue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

皇甫轩 2024-10-04 18:17:34

在任何初始化器运行之前,所有全局 POD 数据都保证被初始化为常量值。

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

编辑:具体而言,根据标准(3.6.2.2:非本地对象的初始化):

一起,零初始化和
调用常量初始化
静态初始化;所有其他
初始化是动态的
初始化。静态初始化
应在任何动态之前执行
进行初始化。

All global POD data is guaranteed to be initialized to a constant value before any initializers run.

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. Then the initializers run, including your register calls.

Edit: Specifically, from the standard (3.6.2.2: Initialization of non-local objects):

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.

濫情▎り 2024-10-04 18:17:34

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

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

在 a.hpp 中:

static int a = 1;

在 b.hpp 中:

extern int a;
static int b = a;

静态变量初始化的顺序没有明确定义,因此在此示例中 b 可能为 1,也可能不是 1。只要您的变量不相互依赖,就可以了。此外,如果您没有给出初始值,静态成员默认设置为零。

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:

static int a = 1;

in 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. 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.

孤芳又自赏 2024-10-04 18:17:34

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

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

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

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

I've tended to see the 'instance' method of Factory implemented as follows:

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. 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. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文