ordering of constructors

Jay Freeman (saurik) saurik@saurik.com
Fri Apr 9 17:26:00 GMT 2004


Theodore:

Ok, I just typed up a massive explanation of why I don't do this due to what
I thought was something wrong with the standard and then I thought about it
for a second and was like "wait, no, that ordering _does_ make sense" and
realized "no, this is a bug in gcc, isn't it".

#include <iostream>

struct A {
    ~A() { std::cout << "~A()" << std::endl; }
};

A &a() {
    static A value;
    return value;
}

struct B {
    B() { a(); }
    ~B() { std::cout << "~B()" << std::endl; }
};

B b;

int main() {
    std::cout << "main()" << std::endl;
    return 0;
}

Accourding to 3.6.3:

Destructors (12.4) for initialized objects of static storage duration
(declared at block scope or at namespace scope) are called when returning
from main and when calling exit (18.3). These objects are destroyed in the
reverse order of the completion of their constructors. For an object of
array or class type, all subobjects of that object are destroyed before
any local object with static storage duration initialized during the
construction of the subobjects is destroyed."

So in this case, the output should be:

main()
~B()
~A()

b's constructor is called; but before that constructor completes, the
constructor for an A is started _and_ completed. The instance of B doesn't
complete construction until after the instance of A does, so reverse order
of completion should destroy the B and _then_ the A.

But instead I keep getting (from a 3 week old copy of 3.4 from CVS):

main()
~A()
~B()

This means that if B's deconstructor uses the instance of A it got (which
comes up all the time in my code) it fails as the object it wants has
already been deconstructed.

So, instead, I have this _horrible_ hack whereby I just don't call the
deconstructors of my most critical singletons (such as my memory manager and
profiler) while still avoiding doing heap allocation for them (as heap
allocating the object that's doing my heap allocation causes... issues...
hehehe):

template <typename Type_>
struct StaticPermanent {
    static Type_ &GetInstance() {
        static union {
            typename etl::PlainOldAlignment< etl::Cons<Type_> >::Result
align;
            char data[sizeof(Type_)];
        };

        // XXX: this obviously needs a lock
        static bool allocated(false);
        if (!allocated) {
            allocated = true;
            new (data) Type_();
        }

        return *reinterpret_cast<Type_ *>(data);
    }
};

Sincerely,
Jay Freeman (saurik)
saurik@saurik.com

----- Original Message ----- 
From: "Theodore Papadopoulo" <Theodore.Papadopoulo@sophia.inria.fr>
To: "Gabriel Dos Reis" <gdr@integrable-solutions.net>
Cc: "Neal Becker" <ndbecker2@verizon.net>; <gcc@gcc.gnu.org>
Sent: Friday, April 09, 2004 9:24 AM
Subject: Re: ordering of constructors


>
> > Why not put a function call in the constructor for A that make sure B
> > initialized?
>
> A simpler technique is to wrap the static members into functions
> returning the static member ie:
>
> struct A {
>
>     typedef whatever B;
>
>     static B& get_member() {
>          static B static_member;
>          return B;
>     }
> };
>
> This has solved the problem of static member ordering for me once
> for all. I just wonder why this approach is not used transparently
> by the compiler for all static members.... Maybe there is some hidden
> cost I do not see... Presumably, the implementation of function static
> members requires a boolean per member... Never went to see how this
> is done.
>
> --------------------------------------------------------------------
> Theodore Papadopoulo
> Email: Theodore.Papadopoulo@sophia.inria.fr Tel: (33) 04 92 38 76 01
>  --------------------------------------------------------------------



More information about the Gcc mailing list