Re: Order of destruction of static members and static objects

Discussion in 'C++' started by Victor Bazarov, Nov 27, 2009.

  1. Juha Nieminen wrote:
    > Assume we have a class like this:
    >
    > class A
    > {
    > class Container;
    > static Container container;


    This is a declaration. Where is the definition?

    >
    > public:
    > ~A();
    > };
    >
    > Assume that A::~A() does something with A::container (eg. removes or
    > changes an element from it or whatever).
    >
    > Also assume that somewhere else there's a global instantiation of A
    > like this:
    >
    > namespace { A anObject; }
    >
    > The question is: Is it guaranteed that 'anObject' will be destroyed
    > before 'A::container'?


    No, it's not guaranteed. What's guaranteed, however, is that if they
    are created in some order, then they are destructed in the reverse order.

    > Because, obviously, if 'A::container' is destroyed first, then the
    > destructor of 'anObject' will be accessing a destroyed Container object.


    Yep.

    > If it's not guaranteed, then how can one use static members safely?


    Apparently you need 'A::container' to be a singleton. See any of the
    existing implementations of that pattern. Most simple ones actually
    create the singleton on demand (in freestore) and never destroy it.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Nov 27, 2009
    #1
    1. Advertising

  2. Victor Bazarov

    James Kanze Guest

    On Nov 27, 2:49 pm, Victor Bazarov <> wrote:
    > Juha Nieminen wrote:
    > > Assume we have a class like this:


    > > class A
    > > {
    > > class Container;
    > > static Container container;


    > This is a declaration. Where is the definition?


    You ask the necessary question.

    > > public:
    > > ~A();
    > > };


    > > Assume that A::~A() does something with A::container (eg.
    > > removes or changes an element from it or whatever).


    > > Also assume that somewhere else there's a global
    > > instantiation of A like this:


    > > namespace { A anObject; }


    > > The question is: Is it guaranteed that 'anObject' will be
    > > destroyed before 'A::container'?


    > No, it's not guaranteed. What's guaranteed, however, is that
    > if they are created in some order, then they are destructed in
    > the reverse order.


    And then answer it without knowing the answer. If the
    definition is in the same translation unit, before this
    statement, it is guaranteed. Otherwise not.

    > > Because, obviously, if 'A::container' is destroyed first,
    > > then the destructor of 'anObject' will be accessing a
    > > destroyed Container object.


    > Yep.


    I'd have to see the source of A::~A() first, before I was so
    categorical:). And also the declaration of Container. If
    A::~A() doesn't access container, there's no problem. Same
    thing if Container has a trivial destructor. (I'll admit that
    with a name like Container, it's highly unlikely that the
    destructor is trivial.)

    > > If it's not guaranteed, then how can one use static members
    > > safely?


    > Apparently you need 'A::container' to be a singleton. See any
    > of the existing implementations of that pattern. Most simple
    > ones actually create the singleton on demand (in freestore)
    > and never destroy it.


    That's often the simplest and most appropriate solution. If
    Container is a more general class (e.g. an std::vector), then
    you can wrap it in a simple function to ensure the same
    behavior. Another solution is to ensure that Container has a
    trivial destructor.

    --
    James Kanze
    James Kanze, Nov 27, 2009
    #2
    1. Advertising

  3. James Kanze wrote:

    > On Nov 27, 2:49 pm, Victor Bazarov <> wrote:
    >> Juha Nieminen wrote:
    >> > Assume we have a class like this:

    >
    >> > class A
    >> > {
    >> > class Container;
    >> > static Container container;

    >
    >> This is a declaration. Where is the definition?

    >
    > You ask the necessary question.
    >
    >> > public:
    >> > ~A();
    >> > };

    >
    >> > Assume that A::~A() does something with A::container (eg.
    >> > removes or changes an element from it or whatever).

    >
    >> > Also assume that somewhere else there's a global
    >> > instantiation of A like this:

    >
    >> > namespace { A anObject; }

    >
    >> > The question is: Is it guaranteed that 'anObject' will be
    >> > destroyed before 'A::container'?

    >
    >> No, it's not guaranteed. What's guaranteed, however, is that
    >> if they are created in some order, then they are destructed in
    >> the reverse order.

    >
    > And then answer it without knowing the answer. If the
    > definition is in the same translation unit, before this
    > statement, it is guaranteed. Otherwise not.
    >

    It is always guaranteed if i interpret the Standard correctly. 3.6.3/1:

    "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."
    Johannes Schaub (litb), Nov 27, 2009
    #3
  4. Victor Bazarov

    James Kanze Guest

    On Nov 28, 3:36 pm, Juha Nieminen <> wrote:
    > James Kanze wrote:
    > > That's often the simplest and most appropriate solution. If
    > > Container is a more general class (e.g. an std::vector),
    > > then you can wrap it in a simple function to ensure the same
    > > behavior. Another solution is to ensure that Container has
    > > a trivial destructor.


    > How can you implement safely something like this?


    > // Header file
    > // -----------
    > class A
    > {
    > static std::vector<int> sharedContainer;


    > A(const A&);
    > A& operator=(const A&);


    > public:
    > A();
    > ~A();
    > };


    > // Source file
    > // -----------
    > std::vector<int> A::sharedContainer;


    > A::A() { sharedContainer.push_back(0); }
    > A::~A() { sharedContainer.pop_back(); }


    > If somewhere else you have something like:


    > namespace { A a; }


    > then the constructor might be accessing an unconstructed
    > std::vector, and the destructor might be accessing a destroyed
    > std::vector.


    Yes. And the push_back/pop_back mean that you can't use int[]
    and static initialization:).

    > Construction safety could be ensured by changing the container to:
    >
    > namespace
    > {
    > std::vector<int>& sharedContainer()
    > {
    > static std::vector<int> container;
    > return container;
    > }
    > }


    > But does that ensure that it's not accessed after it has been
    > destroyed? If not, how do you make sure it's not?


    std::vector< int >&
    sharedContainer()
    {
    static std::vector< int >* theOneAndOnly = new std::vecctor< int >;
    return *theOneAndOnly;
    }

    The standard singleton idiom, in fact.

    --
    James Kanze
    James Kanze, Nov 30, 2009
    #4
  5. Victor Bazarov

    James Kanze Guest

    On Nov 28, 3:26 pm, Juha Nieminen <> wrote:
    > Victor Bazarov wrote:
    > > Apparently you need 'A::container' to be a singleton. See
    > > any of the existing implementations of that pattern. Most
    > > simple ones actually create the singleton on demand (in
    > > freestore) and never destroy it.


    > You mean that if you want to make sure that a static member is
    > never accessed after it has been destroyed, you have to
    > allocate it dynamically and purposefully leak it?


    That's the usual singleton idiom.

    > Doesn't that make static members a bit useless?


    Not really. At the application level, there are any number of
    classes which simply can't be used before entering main (for
    various reasons), and for which all instances will be destructed
    before leaving main. No problem with static members here. It
    generally only gets tricky for library code (where you don't
    really know where it will be used), or a few special application
    level classes.

    --
    James Kanze
    James Kanze, Nov 30, 2009
    #5
  6. Victor Bazarov

    Balog Pal Guest

    "Juha Nieminen" <>
    >> That's the usual singleton idiom.

    >
    > I would have never thought at a programming idiom requires deliberate
    > introduction of a memory leak into your program. Is it really the only
    > way?


    Look up the Alexandrescu-singleton
    Balog Pal, Nov 30, 2009
    #6
  7. Juha Nieminen wrote:
    > James Kanze wrote:
    >>> But does that ensure that it's not accessed after it has been
    >>> destroyed? If not, how do you make sure it's not?

    >> std::vector< int >&
    >> sharedContainer()
    >> {
    >> static std::vector< int >* theOneAndOnly = new std::vecctor< int >;
    >> return *theOneAndOnly;
    >> }
    >>
    >> The standard singleton idiom, in fact.

    >
    > So you mean that you have to deliberately introduce a memory leak if
    > you want to make sure the container is not accessed after it has been
    > destroyed?
    >
    > Is there *really* no better way of doing it?


    How about this:

    class SharedContainer
    {
    public:
    SharedContainer()
    {
    if ( 0 == Counter() )
    {
    c() = new Container;
    }
    ++Counter();
    }
    ~SharedContainer()
    {
    --Counter();
    if ( 0 == Counter() )
    {
    delete c();
    }
    }

    Container& GetContainer()
    {
    assert( NULL != c() );
    return *c();
    }

    private:


    Container *& c()
    {
    static Container *c = NULL;
    return c;
    }
    int& Counter()
    {
    static int n = 0;
    return n;
    }
    };

    warning : I haven't tested this, but I am sure you got an idea ;)


    --
    ultrasound www.ezono.com
    Vladimir Jovic, Nov 30, 2009
    #7
  8. Victor Bazarov

    James Kanze Guest

    On Nov 30, 3:21 pm, Juha Nieminen <> wrote:
    > James Kanze wrote:
    > >> But does that ensure that it's not accessed after it has been
    > >> destroyed? If not, how do you make sure it's not?


    > > std::vector< int >&
    > > sharedContainer()
    > > {
    > > static std::vector< int >* theOneAndOnly = new std::vecctor< int >;
    > > return *theOneAndOnly;
    > > }


    > > The standard singleton idiom, in fact.


    > So you mean that you have to deliberately introduce a memory
    > leak if you want to make sure the container is not accessed
    > after it has been destroyed?


    How are you defining "memory leak". I don't see anything which
    could be qualified as a memory leak, with any of the usual
    definitions.

    The only useful definition is "memory which leaks"; i.e. which
    is periodically reallocated, with previous allocations going
    unused, so that the program grows indefinitely. That's not the
    case here. I have seen it defined as memory which can no longer
    be accessed, because there are no more pointers to it; the only
    use I can think of for this definition is to prove that you
    can't get a memory leak with garbage collection. But the above
    doesn't leak by this definition either, since there's always a
    pointer to it.

    > Is there *really* no better way of doing it?


    It being? And "better" meaning?

    If the goal is to ensure that an object is not destructed (which
    is what you want here), then I don't know of any better way of
    doing it than using an allocation without a delete.

    --
    James Kanze
    James Kanze, Nov 30, 2009
    #8
  9. Victor Bazarov

    James Kanze Guest

    On Nov 30, 3:23 pm, Juha Nieminen <> wrote:
    > James Kanze wrote:
    > > On Nov 28, 3:26 pm, Juha Nieminen <> wrote:
    > >> Victor Bazarov wrote:
    > >>> Apparently you need 'A::container' to be a singleton. See
    > >>> any of the existing implementations of that pattern. Most
    > >>> simple ones actually create the singleton on demand (in
    > >>> freestore) and never destroy it.


    > >> You mean that if you want to make sure that a static member
    > >> is never accessed after it has been destroyed, you have to
    > >> allocate it dynamically and purposefully leak it?


    > > That's the usual singleton idiom.


    > I would have never thought at a programming idiom requires
    > deliberate introduction of a memory leak into your program.


    Where is the memory leak? You never allocate more than one, so
    there can't be a memory leak.

    > Is it really the only way?


    If you want something available until all user defined
    destructors have terminated, you can't invoke a user defined
    destructor on it: either it has a trivial destructor, or you
    don't destruct it. There are no other choices.

    --
    James Kanze
    James Kanze, Dec 7, 2009
    #9
  10. Victor Bazarov

    James Kanze Guest

    On Dec 5, 8:09 am, Paavo Helde <> wrote:
    > Juha Nieminen <> wrote
    > innews:hf0o2g$aut$:
    > > James Kanze wrote:
    > >> On Nov 28, 3:26 pm, Juha Nieminen <> wrote:
    > >>> Victor Bazarov wrote:
    > >>>> Apparently you need 'A::container' to be a singleton.
    > >>>> See any of the existing implementations of that pattern.
    > >>>> Most simple ones actually create the singleton on demand
    > >>>> (in freestore) and never destroy it.


    > >>> You mean that if you want to make sure that a static
    > >>> member is never accessed after it has been destroyed, you
    > >>> have to allocate it dynamically and purposefully leak it?


    > >> That's the usual singleton idiom.


    > > I would have never thought at a programming idiom requires
    > > deliberate introduction of a memory leak into your program.
    > > Is it really the only way?


    > If one wants the singleton to be available until the program
    > termination, then one cannot destroy it from inside the
    > program, it's as simple as that. I would not call this a real
    > memory leak as any decent OS will reclaim the singleton memory
    > immediately afterwards, during the process termination.


    > Of course, one could define a safe order of destruction of all
    > singletons and ensure that they are destroyed properly before
    > the program termination, but this would only make the program
    > slower and more error- prone, with no positive effect
    > whatsoever.


    > This is a typical confusion of the goal and the means. The
    > "memory leak" concept is just a means used for making the
    > programs better, and not a goal in itself.


    On the programs I work on, a memory leak is a fatal error. This
    isn't a memory leak, however, by any definition of the word.

    --
    James Kanze
    James Kanze, Dec 7, 2009
    #10
  11. Victor Bazarov

    James Kanze Guest

    On Dec 7, 6:09 pm, Paavo Helde <> wrote:
    > James Kanze <> wrote in news:0cca4033-dfff-4c8f-
    > :


    [...]
    > >> This is a typical confusion of the goal and the means. The
    > >> "memory leak" concept is just a means used for making the
    > >> programs better, and not a goal in itself.


    > > On the programs I work on, a memory leak is a fatal error. This
    > > isn't a memory leak, however, by any definition of the word.


    > Sure it is, a conceivable definition is just "anything flagged as a
    > memory leak by the diagnostic tool I happened to use".


    But does any tool flag this as a memory leak? There's still a
    pointer to the memory, so it is at most a "possible memory
    leak".

    > I tried to argue why such a definition would not be a useful
    > one.


    The only useful one is the one which uses leak in its everyday
    sense, of course. A little bit constantly seeping out over
    time. The only other one I've seen is "no remaining pointers to
    the memory", which is sometimes used by Java advocates to
    "prove" that Java can't leak memory.

    Here, it's not so much a case of whether the definition is
    useful or not; it's a case of using a totally arbitrary
    definition which doesn't correspond to anything real. (I'm not
    blaming Juha for this---he doubtlessly got it from somewhere.
    But it's not an acceptable definition.)

    --
    James Kanze
    James Kanze, Dec 8, 2009
    #11
  12. Victor Bazarov

    James Kanze Guest

    On Dec 8, 6:16 pm, Paavo Helde <> wrote:
    > James Kanze <> wrote in news:7c0974d7-da79-42d7-
    > :
    > > On Dec 7, 6:09 pm, Paavo Helde <> wrote:
    > >> James Kanze <> wrote in news:0cca4033-dfff-4c8f-
    > >> :


    > > [...]
    > >> >> This is a typical confusion of the goal and the means.
    > >> >> The "memory leak" concept is just a means used for
    > >> >> making the programs better, and not a goal in itself.


    > >> > On the programs I work on, a memory leak is a fatal
    > >> > error. This isn't a memory leak, however, by any
    > >> > definition of the word.


    > >> Sure it is, a conceivable definition is just "anything
    > >> flagged as a memory leak by the diagnostic tool I happened
    > >> to use".


    > > But does any tool flag this as a memory leak? There's still
    > > a pointer to the memory, so it is at most a "possible memory
    > > leak".


    > MSVC++ Debug heap facility (guided by _CrtSetDbgFlag() et al.) is
    > reporting things like this:


    > Detected memory leaks!
    > Dumping objects ->
    > {59831} normal block at 0x0C061168, 36 bytes long.
    > Data: < ÍÍÍÍUI ÍÍÍÍÍ> 00 00 00 00 CD CD CD CD 55 49 00 CD CD CD CD CD
    > {59830} normal block at 0x0C0610E8, 68 bytes long.
    > Data: <Øm Øm > D8 6D 03 0C 08 05 06 0C D8 6D 03 0C 00 00 00 00
    > {59803} normal block at 0x0C060588, 36 bytes long.
    > Data: < ÍÍÍÍUI ÍÍÍÍÍ> 00 00 00 00 CD CD CD CD 55 49 00 CD CD CD CD CD
    > {59802} normal block at 0x0C060508, 68 bytes long.
    > ...


    If there is still a pointer to the object with static lifetime,
    I'd consider calling it a leak an error. Quality leak detecters
    (like Purify) make the distinction. (To be fair to Microsoft,
    the debug leak detector is just a simple hack, to help a little.
    I don't think Microsoft claims that it is complete and thorough.
    I suspect that it just looks at the heap and reports unfreed
    objects, without trying to determine whether there are pointers
    to them or not---the latter can be a major undertaking without
    access to the source. But they really should change the label
    to something like: "unfreed memory (possible leak?)".)

    > As far as I know, it reports all blocks which have been
    > obtained from the heap, and not explicitly freed by the time
    > of check. A thing of interest is that it does not call them
    > "possible memory leaks".


    In fact, what it detects is unfreed memory, and not leaks.
    Which is something useful, not too difficult to do, with little
    or no impact on the runtime. (Programs under Purify run
    significantly slower.) But they should correct the label.

    --
    James Kanze
    James Kanze, Dec 9, 2009
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. JFCM
    Replies:
    4
    Views:
    5,721
  2. Davlet Panech
    Replies:
    4
    Views:
    336
    Davlet Panech
    Nov 10, 2006
  3. Dennis Jones
    Replies:
    2
    Views:
    310
    Dennis Jones
    Jan 5, 2007
  4. BeautifulMind
    Replies:
    7
    Views:
    641
    Ron Natalie
    Feb 8, 2007
  5. Replies:
    8
    Views:
    381
    James Kanze
    Mar 2, 2009
Loading...

Share This Page