Is a typical C ++ Factory class implementation erroneous?

I need to implement a factory class in C ++, but when I thought about it, I discovered one big problem that I could not solve, and I found out that all examples of factory implementation are just as wrong. Maybe I'm wrong, but please tell me why.

So, here is a 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 it goes but:

The C ++ standard does not allow me to determine 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 must be NULL before the registered variable Apple (or Banana) :: or the Factory::instance method will work with an uninitialized value and behave unpredictably.

So what I will not get here? Does the code really work only by chance? If so, how do I solve the problem?

+4
source share
3 answers

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

So, at the beginning of your program, before any register calls are made and before main starts, the pointer will be NULL, and all bools will be false, automatically. Then initializers are started, including calls to your register.

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

Together, zero initialization and constant initialization static initialization; all other initialization dynamic initialization. Static initialization must be performed before any dynamic initialization is performed.

+11
source

All static variables are initialized before the program starts. They are installed at compile time and baked directly into the executable file.

The only problem 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 initialization order of static variables is not defined, so b may or may not be 1 in this example. As long as your variables are independent of each other, you're fine. In addition, you do not specify an initial value; static members are set to zero by default.

+4
source

I usually looked at the Factory instance method implemented as follows:

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

However, the fact is that all access to the instance goes through the static instance method. Calls for registering two classes of fruits, for example, use Factory :: instance () to get a singleton that guarantees the execution of the initializer for Factory :: factory. In my published alternative implementation, static initialization occurs only the first time the method is called.

Possible problems with Apple :: are registered and Banana :: are registered depending on where they can be used. In the published code, they are not used at all. If used only in apple.cpp and banana.cpp respectively, then there is no problem with the initialization order.

+2
source

All Articles