This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Placement New For Singleton Objects Submitted by

Often with objects following the singleton pattern, you will see them implemented as follows:

    // begin code
    // header
    class Singleton
    {
    public:
    	Singleton() {}
    	~Singleton() {}

    	static Singleton *get();
    };

    // CPP file
    #include "Singleton.h"

    static Singleton sSingleton;

    Singleton *Singleton::get()
    {
    	return &sSingleton;
    }
    // end code

A big problem with this approach is due to the order of initialization of global and static objects. According to the C++ standard, they are initialized in the order listed in the source. But across translation units (files), there are no guarantees about order of initialization.
The problem comes if some other global or static object is dependent on Singleton for its construction. It will call Singleton::get(), and the return value is not guaranteed to point to a valid, initialized object due to order of initialization.
A common solution is to use lazy evaluation and allocate singleton objects from the heap:

    // begin code
    // CPP file
    #include "Singleton.h"

    static Singleton *spSingleton=NULL;

    Singleton *Singleton::get()
    {
    	if(spSingleton == NULL)
    	{
    		spSingleton = new Singleton;
    	}
    	return spSingleton;
    }
    // end code

This solves the order of initialization problem because the Singleton object will be created the first time Singleton::get() is called. Astute readers will notice that the Singleton will never be destroyed, but that can be solved by judicious use of the std::auto_ptr<> class.
The lazy evaluation approach can be problematic, particularly for libraries, because you are assuming that the application is using the default implementations of new and delete. On embedded platforms such as a game console, it is not uncommon for applications to use custom memory managers and completely eschew the built in memory management.
The answer lies in a version of the new operator called placement new. It is defined in the header file <new> and is part of the C++ Standard. Placement new takes as an additional parameter an address to already allocated memory. It properly calls the object's constructor, initializing the object in the memory passed to it.
The following code demonstrates the use of placement new:

    // begin code
    // CPP file
    #include "Singleton.h"
    #include <new

    template <class T class placement_auto_ptr
    {
    public:
    	placement_auto_ptr() : mp(NULL) {}

    	placement_auto_ptr &operator=(T *p)
    	{
    		mp = p;
    		return *this;
    	}
    	~placement_auto_ptr()
    	{
    		// can't call delete because it won't use placement delete
    		if(mp != NULL)
    		{
    			mp-T::~T();
    		}
    	}
    	operator T *() { return mp; }
    	T &operator*() { return mp; }
    	T *operator-() { return *mp; }
    private:
    	T *mp;
    };

    static unsigned char sMem[sizeof(Singleton)];
    static placement_auto_ptr<Singleton spSingleton;

    Singleton *Singleton::get()
    {
    	if(spSingleton == NULL)
    	{
    		// use placement new
    		spSingleton = new(sMem) Singleton;
    	}
    	return spSingleton;
    }
    // end code

The use of placement_auto_ptr<> is optional, but recommended. For example, if Singleton allocates OS resources you probably want the destructor called to close them. Not that we do not use the delete operator, but instead call the destructor explicitly. The reason is calling delete will call the default implementation of delete, which will attempt to free memory that was never allocated!
Placement new for singletons is not the answer for every situation, but it is a useful item to have in your tool chest. It is particularly useful for library authors who can not make assumptions about their client application's memory management.
-Steve Anichini

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.