Sunday, January 25, 2009

Singleton pattern C++. My recipe.

Look into example

#include <iostream>
#include "SingletonPattern.h"


class A : public ISingleton<A>
{
//a few extra movements (friend declarations) is needed to create own non-public default ctor and dtor.
A() { j = 10;};
~A() {std::cout << "~A" << std::endl;};
friend class ISingleton<A>;
friend class std::auto_ptr<A>;

public:
//do not define any public ctors and dtor.
//do not define copy ctor and assign operator at all.

static int j;
void Test(int i) { std::cout << "A::Test(" << i << ") " << ++j << std::endl;};
};
int A::j = 10;


class B : public ISingleton<B>
{
public:
void Test(int i) { static int j = 0; std::cout << "B::Test(" << i << ") " << ++j << std::endl;};
};


class C : public ISingleton<C>
{
public:
virtual void Test(int i) { static int j = 0; std::cout << "C::Test(" << i << ") " << ++j << std::endl;};
};


class D : public C
{
public:
void Test(int i) { static int j = 0; std::cout << "D::Test(" << i << ") " << ++j << std::endl;};
};


int main()
{
A::GetSingleton().Test(5);
A& a = A::GetSingleton();
a.Test(6);
// A anotherA = A::GetSingleton();//compilation error, ok
// a = a;//compilation error, ok

B::GetSingleton().Test(3);
B& b = B::GetSingleton();
b.Test(100);
C::GetSingleton().Test(33);
D::GetSingleton().Test(23);//it doesn't work for D because ISingleton was defined for parent class C
b.Test(15);
}


A::Test(5) 11
A::Test(6) 12
B::Test(3) 1
B::Test(100) 2
C::Test(33) 1
C::Test(23) 2
B::Test(15) 3
~A



There are 3 singleton classes (A, B, C). As you see it's very simple to create singleton class, only inherit public ISingleton<ThisClass>.
A few cautions however are written on lines 7, 14, 15. First belongs to own default ctor and dtor. Well, usually singleton class encapsulates heap objects, so own destructor is needed. Own default constructor maybe also useful, but if initialization with arguments is needed, custom Init method is the case because ctors with arguments are unavailable with this singleton implementation.

Ok, look at the implementation code.

#ifndef _SINGLETON_PATTERN_INTERFACES_SVOLKOV_
#define _SINGLETON_PATTERN_INTERFACES_SVOLKOV_


#include <memory>


template <class T>
class ISingleton
{
protected:
ISingleton();
~ISingleton();
private:
ISingleton(const ISingleton&);
ISingleton& operator= (const ISingleton&);
static std::auto_ptr<T> _ptr;
friend class std::auto_ptr<T>;
public:
///Get singleton instance.
static T& GetSingleton();
};


template <class T>
std::auto_ptr<T> ISingleton<T>::_ptr;


template <class T>
ISingleton<T>::ISingleton()
{
}


template <class T>
ISingleton<T>::~ISingleton()
{
}


template <class T>
T& ISingleton<T>::GetSingleton()
{
if (!_ptr.get())
_ptr = std::auto_ptr<T>(new T());
return *(_ptr.get());
}


#endif

Lazy initialization is used to postpone instantiation process. Standard smart pointer is used to destroy object automatically when program is closing. This instantiation/deletion model is exactly what is usually needed for a singleton manager etc.
Please note that it's single-threaded pattern implementation, not multi-threaded.

No comments:

Post a Comment