Search code examples
c++destructorstatic-initializationinitialization-order

How to do static de-initialization if the destructor has side effects and the object is accessed from another static object's destructor?


There is a simple and well-known pattern to avoid the static initialization fiasco, described in section 10.13 of the C++ FAQ Lite.

In this standard pattern, there is a trade-off made in that either the constructed object gets never destructed (which is not a problem if the destructor does not have important side effects) or the static object cannot safely be accessed from another static object's destructor (see section 10.14 of the C++ FAQ Lite).

So my question is: How do you avoid the static de-initialization fiasco if a static object's destructor has important side effects that must eventually occur and the static object must be accessed by another static object's destructor?


(Note: the FAQ-lite mentions this question is answered in FAQ 16.17 of C++ FAQs: Frequently Asked Questions by M. Cline and and G. Lomow. I do not have access to this book, which is why I ask this question instead.)


Solution

  • function static objects like global objects are guaranteed to be destroyed (assuming they are created).

    The order of destruction is the inverse of creation.
    Thus if an object depends on another object during destruction you must guarantee that it is still available. This is relatively simple as you can force the order of destruction by making sure the order of creation is done correctly.

    The following link is about singeltons but describes a similar situation and its solution:
    Finding C++ static initialization order problems

    Extrapolating to the general case of lazy initialized globals as described in the FAQ lite we can solve the problem like this:

    namespace B
    {
        class B { ... };
    
        B& getInstance_Bglob;
        {
            static B instance_Bglob;
            return instance_Bglob;;
        }
    
        B::~B()
        {
             A::getInstance_abc().doSomthing();
             // The object abc is accessed from the destructor.
             // Potential problem.
             // You must guarantee that abc is destroyed after this object.
             // To gurantee this you must make sure it is constructed first.
             // To do this just access the object from the constructor.
        }
    
        B::B()
        {
            A::getInstance_abc();
            // abc is now fully constructed.
            // This means it was constructed before this object.
            // This means it will be destroyed after this object.
            // This means it is safe to use from the destructor.
        }
    }
    namespace A
    {
        class A { ... };
    
        A& getInstance_abc()
        {
            static A instance_abc;
            return instance_abc;
        }
    }