代码之家  ›  专栏  ›  技术社区  ›  kovarex

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

  •  4
  • kovarex  · 技术社区  · 14 年前

    我需要在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 指针需要定义为空 之前 苹果(或香蕉)::注册变量,或 Factory::instance 方法将使用未初始化的值,并且行为不可预测。

    那么,我为什么不来这里?代码真的只有在意外情况下才起作用吗?如果是,我应该如何解决这个问题?

    3 回复  |  直到 9 年前
        1
  •  11
  •   SoapBox    14 年前

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

    因此,在程序开始时,在进行任何寄存器调用之前以及在运行main之前,指针是空的,所有的bools都是假的。然后运行初始值设定项,包括寄存器调用。

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

    一起,零初始化和 调用常量初始化 静态初始化;所有其他 初始化是动态的 初始化。静态初始化 应在任何动态 初始化发生。

        2
  •  4
  •   JoshD    14 年前

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

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

    在A.HPP中:

    static int a = 1;
    

    在B.HPP中:

    extern int a;
    static int b = a;
    

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

        3
  •  2
  •   imaginaryboy    14 年前

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

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

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

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