Once you start to optimize your code, read about patterns, and so on, you realize it is an interesting idea using static data members
to keep a single copy of something you may use in all the instances of your class (instead of having a copy in each object).
Sometimes those static data members are very simple, like constants or others, but when that information gets more and more complex,
then you realize on the limitations of the C++ language:
- Static data members must be initialized outside the class body, generally in *.cpp files, except const integral-type
static data members. It is a great inconvenience for inline classes and templates defined in one *.h file.
- C++ doesn't have static constructors, as Java or C# does, so you usually have to initialize the static data members one by one
(independently). This is a limitation because you may want to initialize several static data members in the same loop or algorithm, for example.
I decided to workaround both limitations, and here is the result.
I previously discoursed the first limitation
in this topic at Stack Overflow: static constructors in C++?
need to initialize private static objects.
About the second limitation, it is interesting to read this other topic: What is the
rationale for not having static constructors in C++?. Here is my point of view on this:
- Although C++ was not intended to have static constructors in the beginning, it is still interesting to have the possibility to extend
the language to work in the same way as Java and C#, as it is the free will of the programmer to use that or not.
- C++ is an older language than Java and C#, static constructors were invented later, but there is no technical reason why they cannot be implemented now.
- There are ways to call functions to initialize static data members one by one, but it is not the same as initializing several static data members
at the same time in the same algorithm (in a static constructor for example). That can also be done in a
static Init() function member...
just name it
StaticConstructor() and create a mechanism to call it automatically at startup and you got it!
- Java and C# are managed languages, so they do not need static destructors. But native C++ is different. As a non-managed language,
it should have a destructor for each constructor, as there is no garbage collector in native C++. If the programmer calls
new at the constructor, we should be able to call
delete at the destructor. That is the reason I also implemented the static destructor.
Using the Code
The way of using the code is very simple:
- Include the provided StaticConstructor.h.
StaticDestructor as static function members of your class.
- Invoke them with the macro outside of the class body.
static void StaticConstructor()
static void StaticDestructor()
You may also use the macros
STATIC_DESTRUCTOR() as an alias for
static void StaticConstructor() and
static void StaticDestructor() respectively, but this only works
for inline classes (usually declared only in .h files) or the header declaration.
To declare the implementation in a .cpp file, you should use the full expressions
static void MyClass::StaticConstructor()
static void MyClass::StaticDestructor(). The idea is also to invoke the static constructor in only one .cpp file
(whenever possible) to avoid several invokes to it. You may download the source and examples of all these.
Apart from that, I also implemented macros for a fast static start-up code (without the need to declare a static constructor in a dummy class):
std::cout << "Starting up..." << std::endl;
std::cout << "Finishing up..." << std::endl;
Using this kind of static constructor in templates is possible, but it is important to invoke separately the static constructor of each template instance in this way:
typedef MyTemplate<int> MyTemplateInt;
typedef MyTemplate<double> MyTemplateDouble;
This is usually done in a different .cpp file from the template definition, which is usually in a .h file.
If you also need to initialize some data members for the template, you may do it in the same .cpp file, also for
each template instance... but this could be a great inconvenience. You may prefer to encapsulate the initialization inside the template code, because:
- You keep together the template code and its initialization.
- You do not need to repeat the initialization code for each instance of the template.
- You may not want to put that code in a .cpp file.
But this cannot be done in C++, so I tried a workaround. Instead of using a data member, I used a function member to store data.
I called this: "data function member" (DF member), whose implementation is:
static TypeName& DFMemberName()
static TypeName DFMemberName(InitValue);
In the examples, I use the macro
STATIC_DF_MEMBER(TypeName, DFMemberName, InitValue) to declare them in an easier way.
You can declare the DF members in the class or template header declaration, and access them in the static constructor and the
static destructor. You may use them as a reference to your data members (in fact, they are function members that return references
to your data), so this kind of code is valid:
DFMemberName() = Value2;.
The DF members are initialized the first time you access them, not when you declare them (they are not normal data members), so you may want
to access them at the static constructor to ensure they are initialized at the beginning of the execution.
Points of Interest
Implementation: I used a trick in the macro that invokes the static constructor - I declare a global variable (that would be constructed
at startup) to put some code in its default constructor (a call to the static constructor of your class). The destructor of that class would call the static destructor of your class.
Order problem: The compiler will process the calls to the data member initialization and the static constructors in a particular order
that could be compiler-dependant. Be very careful with that, otherwise the data members may not be initialized the way you want.
In Microsoft Visual Studio, the order seems to be the order of the lines of code in the file, so make sure you put the call
INVOKE_STATIC_CONSTRUCTOR after the data member initialization.
- v1.2 - 2011/11/27: First release with some improvements and a full example.