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

3

4

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.)

Radiomicrometer answered 29/6, 2010 at 17:26 Comment(3)
See here: #335869Plaintive
Thanks, that's the point I was missing...Radiomicrometer
C++ FAQ Lite moved to dietmar-kuehl.de/mirror/c++-faqHardening
P
5

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;
    }
}
Plaintive answered 29/6, 2010 at 17:58 Comment(3)
One of the fixes from C++0x is deterministic destruction order of function local static variables. So in C++03, you have no guarantees that A will be destructed after B even if A was constructed before B. The one notable exception is when A and B are defined in the same compilation unit.Pinch
@Caspin: In the current standard "ISO/IEC 14882" (openassist.googlecode.com/files/… is well defined. See section "3.6.3 Termination" Paragraph 1: <quote>Destructors (12.4) for initialized objects of static storage duration (declared at block scope or at namespace scope) are called as a result of returning from main and as a result of calling exit (18.3). These objects are destroyed in the reverse order of the completion of their constructor or of the completion of their dynamic initialization.</quote>Plaintive
It looks like you are correct. Destruction order is always inverse construction order. I was under the impression that C++ only guaranteed within a compilation unit, not the whole program. I should read the spec more often. My comment about C++0x was me being confused about the new multithreaded destruction order introduced with the new standard.Pinch
C
0

As long as the other object's static destructor runs first, you're ok. You can assure this by having the other object get constructed before "object A". As long as both objects are declared in the same compilation unit, they will be initialized in the order they appear in the source, and destructed in the opposite order.

If you need this to happen across compilation units, you're out of luck. Better is to create them dynamically at runtime and destroy them at the end of main, rather than making them static.

Casares answered 29/6, 2010 at 17:36 Comment(1)
The OP specifically quotes a link to a pattern that avoids the necessity of the globals being in the same compilation unit. Using the same technique he can get a defined construction order and thus a defined destruction order thus he is not out of luck.Plaintive
L
0

It is a bit of a hack but I would add some static bools to keep track of the order of de initialization Then the object that ends up last does the clean up even if it isn't the owner.

Leges answered 29/6, 2010 at 17:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.