below code illustrates the Phoenix Singleton described in Andrey Alexandrescu's Modern C++ Design book.
Singleton& Instance()
{
if (!pInstance_)
{
// Check for dead reference
if (destroyed_)
{
OnDeadReference();
}
else
{
// First call—initialize
Create();
}
}
return pInstance_;
}
void Singleton::OnDeadReference()
{
// Obtain the shell of the destroyed singleton
Create();
// Now pInstance_ points to the "ashes" of the singleton
// - the raw memory that the singleton was seated in.
// Create a new singleton at that address
new(pInstance_) Singleton;
// Queue this new object's destruction
atexit(KillPhoenixSingleton);
// Reset destroyed_ because we're back in business
destroyed_ = false;
}
static void Create();
{
// Task: initialize pInstance_
static Singleton theInstance;
pInstance_ = &theInstance;
}
void Singleton::KillPhoenixSingleton()
{
// Make all ashes again
// - call the destructor by hand.
// It will set pInstance_ to zero and destroyed_ to true
pInstance_->~Singleton();
}
virtual ~Singleton()
{
pInstance_ = 0;
destroyed_ = true;
}
Singleton* Singleton::pInstance_ = 0;
bool Singleton::destroyed_ = false;
Quote from the book:
Let's analyze the flow of events. During the application exit sequence, Singleton's destructor is called. The destructor resets the pointer to zero and sets destroyed_ to true. Now assume some global object tries to access Singleton again. Instance calls OnDeadReference. OnDeadReference reanimates Singleton and queues a call to KillPhoenixSingleton, and Instance successfully returns a reference to a valid Singleton object. From now on, the cycle may repeat.
My question is - if we assign to pointer pInstance_ = 0
in the destructor, than it assigns to a local static reference which should be deleted by that point, how that we place a new object at 0 address? Please tell me that I am missing something. If someone can explain me the flow, I would really appreciate. Thanks
The "Phoenix" name comes from the legendary bird that died, caught fire and was born again from its ashes.
In this case, yes, the Singleton can die, the destructor runs, pInstance
becomes nullptr
, and it's gone - until it needs to revive. When that happens, Create
makes pInstance
non-null before new (pInstance) Singeton
recreates the object.
[edit]
You need to know the rules for object lifetimes and storage duration. Obviously, the storage for an object must be allocated before an object is created, and the storage must not be deallocated until the object is destroyed. The interval in between is known as the storage duration. Now, there is such a thing as static storage duration. Such storage isn't reclaimed until the program exits, even if the object in that storage is destroyed. Variables defined at namespace scope ("globals") and static
variables have this static storage duration.
Therefore, the storage for theSingleton
has static storage duration, and pInstance
can point to this storage even if the object there is destroyed
. Note that the destroyed
and pInstance
variables themselves also have static storage duration, as they're globals.