Re: Simple question on Pointers
"Igor Tandetnik" <itandetnik@mvps.org> wrote in message
news:OWR%23$YbVJHA.3912@TK2MSFTNGP06.phx.gbl...
I'm talking about function-static objects, like this:
Singleton* getInstance() {
static Singleton instance;
return &instance;
}
I think I've got it! I mean, I think I understand why/how it was possible
for 'B' to destruct before the last 'A'. It's that "smart pointer" business
I was mentioning to "Tommy".
See, there's actually 3 relevant objects involved here which I will simply
call:
A [an "inifile" wrapper class]
B [the map of filenames to actual inifiles (see below)]
C [the actual file data to be shared by instances of A
- cached in memory, copied via smart pointer]
Now when you create an instance of A, it will go and access a static member
of C which will in-turn call up B (the singleton) to try to find a C* to
bind with A. If a match is found it returns an existing C*, otherwise
creates a new one. Same old boring stuff everyone does.
So that means that B will certainly outlive the first instance of C, but
that's all we know so far. C is not (and cannot be) static or global, it is
dynamically allocated and turned into a smart pointer. You can never declare
a C (private constructor). But... that's not the main point because A's
require C*'s so they still need B at some point before construction
completes.
Here's the key difference: We can build ourselves a global A without having
to construct a B. Furthermore, we can construct a non-empty global A without
making any reference to B in the constructor!
Here are the 2 ways:
1. Declare an empty one (no lookups)
2. Invoke it's copy constructor through a "temporary 'A'"
So if we declare a static global A by copy construction, then later on when
we access it, we'll do so by first constructing a valid, *but temporary* 'A'
and the construction of global A becomes a simple matter of copying over a
pointer and incrementing a reference count. This happens *after* the first B
has been created (ie. our temp).
And that (in fact) is EXACTLY what the ?dubious? function in question does!
And here it is:
TwInifile::SECTION& GetSpecialSection()
{
static TwInifile::SECTION sect = TwInifile::SECTION(GetGlobalInifile(),
"TheSpecialSection");
return sect;
}
Not easy to read, but the breakdown is this:
GetGlobalInifile() returns our temp 'A' (a TwInifile on the stack).
TwInifile::SECTION is just a wrapper around an inifile that maintains a
subsection name and a TwInifile for direct access (it's all smart pointers
so I can copy them around any way I want).
---------------------
So as you can see, although 'sect' is a static global, it's creation is
invoked via a temporary 'A' (TwInifile) which in turn allocates (if
necessary) a 'C*' (pointer to data) which may (or may not) construct 'B'. If
it happens that B gets constructed here, then it occurs *before* the copy
constructor is invoked and hence 'sect' is scheduled to destruct *after* 'B'
(the map of filenames to C*'s).
So when I went back and reverted the "singleton B" interface; putting the
variable B back to global scope, it constructed before anyone did *anythng*!
In that way B managed to jump in ahead of A's construction (above : 'sect')
thus solving the problem (which I'm only now fully beginning to understand).
So that's the reason. Very subtle, but not the compiler's or linker's fault,
and definitely not "random" as I had previously thought. Tough bug to find
though... so I'm glad I gave it some time here and discovered the actual
reason why I had to move B to global scope.
- Alan Carre