Many C++ programmers have suffered from the fierce clashes with the global C++ objects initialization/cleanup. Eventually I've found a good enough solution to this problem, I've been using (and anjoying) it for years now. My question is: does this solution fully comply to the C++ standard, or it's "platform/implementation-dependent"?
The problem
Generally-speaking there are two major problems with global objects:
- Unpredictable order of their construction/destruction. This bites if those objects depend on each other.
- The construction/destruction code is executed during the CRT initialization/cleanup outside the main program entry point. There's no way to wrap this code with
try
/catch
, or perform any preliminary initialization.
One way to overcome those issues is not using global objects at all. Instead one may use static/global pointers to those objects. During the program initialization those objects are either allocated dynamically or instantiated as automatic variables within the entry point function (main
), and their pointers stored in those pointers. By such you have the full control over your "global" objects lifetime.
However this method also has some drawbacks. It's related to the fact that not only the creation/destruction of those objects is different, but also their access is different. Normally a global object resides in the data section, which is allocated by the loader, and its virtual address is known at the build time. Using global pointers leads to the following drawbacks:
- Somewhat slower object access, extra pointer dereferencing. During the runtime instead of assuming the object is at the specified address the compiler generates the code that dereferences the global pointer.
- Weaker optimizations. The compiler may not realize that the pointer always points to the same object.
- If the actual objects are allocated on heap:
- Worse performance (heap allocations are "heavy")
- Memory fragmentation
- Chance of out-of-memory exception
- If the actual objects are allocated on stack (auto variables in
main
): - Stack size is usually limited. In some circumstances its consumption by "fat" objects is suboptimal.
The solution
The solution I've found is to override the object's new
/delete
operatiors.
// class definition
class MyObject
{
// some members
// ...
static char s_pMyPlaceholder[];
public:
// methods
// ...
static MyObject& Instance()
{
return *(MyObject*) s_pMyPlaceholder;
}
void* operator new (size_t) { return s_pMyPlaceholder; }
void operator delete (void*) {}
};
// object placeholder instantiated
char MyObject::s_pMyPlaceholder[sizeof(MyObject)];
void main()
{
// global initialization
std::auto_ptr<MyObject> pMyObj(new MyObject);
// run the program
// ...
}
The trick is to allocate enough space in the global memory (by declaring a global array of the adequate size), and then use fictive memory allocation for the needed object, that will "allocate" this global memory". By such we achieve the following:
- Semantically we allocate the object dynamically. Hence we have the full control over its lifetime.
- Actually the object resides in the global memory. Hence all the drawbacks related to the "pointer-wise" method are inapplicable to our case.
- The object is visible everywhere in the program. One calls
MyObject::Instance()
to get the reference to it. And, BTW, this function call is easily inlined by the compiler.
So that everything seems ok with this method. I'm just curious if it's legal from the C++ standard perspective.