Re: Threadsafe singletons
"David Barrett-Lennard" <davidbl@iinet.net.au> schrieb im Newsbeitrag
news:1154488230.222001.171700@h48g2000cwc.googlegroups.com...
I have never had an order of destruction problem myself. However I use
singletons rarely and they tend to be used either for caching or for
registries. Can you outline a reasonable example with order of
destruction problems?
I had these order of destruction problems when I used a memory tracking
mechanism to keep track of dynamic strorage allocation and dealltocation.
Here's an example that demonstrates the problem:
// Begin example code
#include <iostream>
template <class T>
class A
{
T* m_ptr;
A() : m_ptr( 0 ) {}
~A() { if ( m_ptr != 0 )
std::cout << "Oops..." << std::endl; }
A( const A& ){}
A& operator=( const A& );
public:
static A& Instance()
{ static A inst; return inst; }
void HoldAddress( T* ptr )
{ m_ptr = ptr; }
void ReleaseAddress()
{ m_ptr = 0; }
};
template <class T>
class B
{
T* m_ptr;
public:
B() : m_ptr( 0 ) {}
~B(){ A<T>::Instance().ReleaseAddress();
delete m_ptr; }
void CreateTrouble()
{ m_ptr = new T;
A<T>::Instance().HoldAddress( m_ptr ); }
};
B<int> b;
int main()
{
b.CreateTrouble();
return 0;
}
// End example code
The example defines to classes named A and B. It's maybe a little easier to
understand if you imagine that in a real program, class A would hold
pointers to allocated storage.
There is one non-local static object of type B<int>, named b. It will be
created first because it is the only non-local static object in the program,
and it is the first object to be used in main(). The call to
B<int>::CreateTrouble() causes the allocation of an int and the construction
of an object of type A<int>. It passes the address of the allocated int to
A<int>::HoldAddress(), which in practice would mean keeping track of
allocated memory.
As objects of static storage duration are destroyed in the reverse order of
the completion of their constructor (see 3.6.3/1), the object of type A<int>
will be destroyed first, causing its destructor to be called. Unfortunately,
the pointer that has been passed to A<int>HoldAddress() has not been removed
from the "database" yet. This is done when the object of type B<int> is
destroyed. Note that B<int>::~B() yields undefined behaviour according to
3.6.3/2, as it calls A<int>::Instance(), which is a function containing a
local static object that has been destroyed.
The problem would not occur if B<T>::CreateTrouble() would be called from
B<T>::B(), because then, the constructor of A<T> would be completed *before*
the constructor of B<T>, causing the object of type B<int> to be destroyed
*before* the object of type A<int>. The pointer that has been passed to
A<int>::HoldAddress() would then be removed before A<int> is destroyed.
I hope I was able to explain things in an understandable way... ;-)
--
Matthias Hofmann
Anvil-Soft, CEO
http://www.anvil-soft.com - The Creators of Toilet Tycoon
http://www.anvil-soft.de - Die Macher des Klomanagers
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]