C++ - Design pattern - Singleton

This Singleton design pattern is certainly the most known and maybe the most easiest to understand.

Its goal is to guarantee that an object will be created only once through all the program.

So this object has to be unique, can't even be created twice nor copied and it must be accessible through the entire application.

This is for example the case when you have a logging class that must be accessible by almost every class in your program.

So to resolve this problem Singleton may be the solution.

Let's discover the Singleton design pattern in this tutorial with two different implementations.

First of all

With the Singleton design pattern we have a static member function instead of a simple global static variable.

The reason is that with global or static variable we cannot be sure that another global variable will be used in another file.

Furthermore it's impossible to know in which order those variables will be instantiated or destroyed.

And another important point is that a global static variable will always be called whereas, with a static member function, if this function is never called it'll never be instantiated.

So using a static member function is an interesting solution to resolve those problems.

The code is straightforward.

We are using a protected constructor because we let possibility, to classes inheriting from the Singleton, to instantiate this Singleton.

We also set the copy constructor as private because we don't want that someone copy the Singleton instance to another address.

The first implementation could be called the classic one, because we will use new and delete keywords.

For the second one, we will use no pointer at all, so this kind of Singleton will be shorter than the classic one.

Code of Singleton with a classic pointer (new/delete)

So main goal is to first call the public static theInstance() method in order to check if one object of type Singleton has been already created.

If we don't have a Singleton instance yet, then we create it by calling its constructor.

If there is already a Singleton instance, then we only return its instance.

This mecanism guarantees uniqueness of the Singleton object.

Furthermore within the constructor we are using the atexit() function when the program exits.

So we use the endOfProgram() callback for that which will delete the instance and reset the _instance address to 0 in order to avoid dangling pointer or bad read memory.

From begin to end: theInstance() > Constructor > exit > endOfProgram() > Destructor > _instance = 0.

The Singleton is created once and destroyed at the end of the program, so no leak.

Moreover we forbid the copy constructor to be used by setting it as private.

Singleton.h

#ifndef __BADPROG_SINGLETON_H__
#define __BADPROG_SINGLETON_H__

// Badprog.com

class Singleton
{
public:
    virtual ~Singleton();
    
    static Singleton *theInstance();
    static void endOfProgram();

protected:
    Singleton();

private:
    Singleton(const Singleton &other);
    static Singleton *_instance;
};

#endif // __BADPROG_SINGLETON_H__

 

Singleton.cpp

#include <iostream>
#include <cstdlib>
#include "Singleton.h"

using namespace std;

// Badprog.com

Singleton *Singleton::_instance = 0;

//-----------------------------------------------------------------------------
// Constructor.
//-----------------------------------------------------------------------------
Singleton::Singleton() {
    cout << "Begin of Singleton." << endl;
    atexit(&(endOfProgram));
}

//-----------------------------------------------------------------------------
// Destructor.
//-----------------------------------------------------------------------------
Singleton::~Singleton() {
    cout << "End of Singleton." << endl;
}

//-----------------------------------------------------------------------------
// Destructor.
//-----------------------------------------------------------------------------
void Singleton::endOfProgram() {
    delete _instance;
    _instance = 0;  // avoids dangling pointer and bad read memory
}

//-----------------------------------------------------------------------------
// Returns the Singleton instance.
//-----------------------------------------------------------------------------
Singleton *Singleton::theInstance() {
    if (0 == _instance) {
        _instance = new Singleton();
    }
    return _instance;
}

 

main.cpp

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

using namespace std;

// Badprog.com

//-----------------------------------------------------------------------------
// Main.
//-----------------------------------------------------------------------------
int main() {
    // Using instance
    Singleton *s1 = Singleton::theInstance();
    Singleton *s2 = Singleton::theInstance();
    Singleton &s3 = *s2;

    // Displaying instance adresses
    cout << s1 << endl;
    cout << s2 << endl;
    cout << &s3 << endl;

return 0;
}

 

Result of classic Singleton

Begin of Singleton.

032ABE40

032ABE40

032ABE40

End of Singleton.

Code of Singleton without any pointer

With this kind of implementation we have no pointer so no need to free the memory at the end of the program because it will be done automatically.

Indeed this time the constructor is called statically so no memory in the heap will be allocated for that.

So each time we will enter in the theInstance() method, only the first instance of the Singleton will be used.

Furthermore we are using the delete specifier (C++11) on the copy constructor to prevent using it.

SingletonShort.h

#ifndef __BADPROG_SINGLETONSHORT_H__
#define __BADPROG_SINGLETONSHORT_H__

// Badprog.com

class SingletonShort
{
public:
    virtual ~SingletonShort();
    static SingletonShort &theInstance();
    SingletonShort(const SingletonShort &other) = delete; // prevents classic copy

protected:
    SingletonShort();
};

#endif // __BADPROG_SINGLETONSHORT_H__

 

SingletonShort.cpp

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

using namespace std;

// Badprog.com

//-----------------------------------------------------------------------------
// Constructor.
//-----------------------------------------------------------------------------
SingletonShort::SingletonShort() {
    cout << "Hello from Badprog :D" << endl;
}

//-----------------------------------------------------------------------------
// Destructor.
//-----------------------------------------------------------------------------
SingletonShort::~SingletonShort() {
    cout << "Goodbye from Badprog :p" << endl;
}

//-----------------------------------------------------------------------------
// Gets instance.
//-----------------------------------------------------------------------------
SingletonShort &SingletonShort::theInstance() {
    static SingletonShort uniqueInstance;
    return uniqueInstance;
}

 

main.cpp

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

using namespace std;

// Badprog.com

//-----------------------------------------------------------------------------
// Main.
//-----------------------------------------------------------------------------
int main()
{
    // Using instance
    SingletonShort &s1 = SingletonShort::theInstance();
    SingletonShort &s2 = SingletonShort::theInstance();
    
    // Displaying instance adresses
    cout << &s1 << endl;
    cout << &s2 << endl;

    return 0;
}

 

Result of shorter Singleton

Hello from Badprog :D

00F3D288

00F3D288

Goodbye from Badprog :p

Conclusion

The Singleton is a good entry point in the design pattern world.

Not useful in all situations, it's sometimes criticized by some programmers to be an anti-pattern.

Anyway, if you understood it, good job you did it! cool

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.