Patterns. Singleton pattern. Overview. Features of use. Implementation in C++




Singleton pattern. Overview. Features of use. Implementation in C++

Before studying this topic, it is recommended that you familiarize yourself with the following topic:


Contents


Search other websites:

1. Singleton pattern (singleton). General information. Responsibilities. Cases of using

The Singleton pattern (singleton, loner) refers to the generative patterns. The Singleton pattern is designed to create a specified number of instances (objects) of a class. The most common use of the Singleton pattern is to create a guaranteed one instance of a class.

The Singleton pattern is important in cases where some classes need a certain number of instances to exist, such as one instance.

The use of global variables does not enforce restrictions on the creation of additional class instances. The global variable gives access to the object, but does not prevent the creation of multiple instances of the class. Therefore, it is advisable to implement these restrictions in the class itself. This is exactly the approach that the Singleton pattern suggests.

The Singleton pattern imposes the following responsibilities on a class:

  • control of the number of copies according to the condition of the problem;
  • return the required number of copies on demand;
  • keeping records of received copies.

Use cases for the Singleton pattern:

  • when you need to create exactly one instance of the class;
  • when you need to create no more than a specified number of class instances;
  • when you need to extend a single instance of a class with derived subclasses. In this case, the client has the opportunity to use the extended tools (operations, methods) of subclasses without modifying their code.

 

2. Block diagram of the Singleton pattern

Most often, in the literature you can find the following structural diagram of the Singleton pattern (Figure 1).

Block diagram of the Singleton pattern

Figure 1. Block diagram of the Singleton pattern

Figure 1 shows the Singleton class, which contains three methods:

  • Instance() is the main method in which the creation of an instance of the class and control over the number of created instances of the class are carried out. If the number of class instances does not exceed a certain maximum (for example, 1), then the method returns an instance of the Singleton class;
  • SingletonOperation() is a class method that does some work. There can be several methods with any names (for example, SingletonOperation1(), SingletonOperation2(), etc.) that implement the functionality of the class;
  • GetSingletonData() – a class method that returns data. A variety of methods can be implemented here depending on the requirements set for the class.

Instance of the class in the client code is created by calling the Instance() function. There is no other way to create an instance of the class.

 

3. An example of the implementation of the Singleton pattern in C++. Creating one instance of a class

The example demonstrates the use of the Singleton pattern on the class of the same name. The following members (elements) are implemented in the Singleton class:

  • internal hidden (private) static variable _instance. This variable stores a pointer to a single instance of the class. As you know, static variables are stored in external memory in one instance. Since this variable is hidden, it is accessed from the class methods;
  • internal hidden variable d. This variable conditionally defines the class data. Optionally, you can change the class data to your own;
  • the Singleton() constructor. The constructor initializes the internal variable d to zero. The constructor is placed in the protected section so that it is impossible to create an instance of the class from other code bypassing the Instance() method, as well as to make it possible to inherit this class. If the client tries to instantiate the Singleton class directly, a compile-time error will occur;
  • static method Instance() – returns an instance of the Singleton class by calling the protected constructor of the class. The pointer to the created instance is stored in the internal variable _instance;
  • public methods Get(), Set() – implement data access (variable d);
  • method Print() – displays the value of internal data (d) for control;
  • destructor ~Singleton() – implements the correct release of memory allocated for a single instance of the class.

The text of the Console Application type program is as follows:

#include <iostream>
using namespace std;

// Implementation of the Singleton pattern in C++.
// Creating at most one instance of the Singleton class
class Singleton
{
private:
  // A static internal variable that holds a single instance of the class.
  // This variable can be accessed from the methods of this class, since
  // it is declared private. There is no access to the variable from the methods of other classes.
  static Singleton* _instance;

  // Some class data, there can be any data here
  int d;

protected:
  // The class constructor declared as protected, in order to:
  // - it was impossible to create an instance of the class directly with the new operator;
  // - it was possible to inherit this class from other classes.
  Singleton()
  {
    d = 0;
  }

public:
  // A static method that returns an instance of the Singleton class.
  // The method checks if an instance has been created
  static Singleton* Instance()
  {
    if (_instance == nullptr) // Has the class been previously instantiated
    {
      // if not, create a single instance and return it.
      _instance = new Singleton();
      return _instance;
    }
    else
    {
      return nullptr;
    }
  }

  // Methods (operations) for accessing internal data of a class (d field)
  int Get()
  {
    return d;
  }

  void Set(int _d)
  {
    d = _d;
  }

  // Method that outputs the value of the internal field d
  void Print()
  {
    cout << "d = " << d << endl;
  }

  // Class destructor - correct memory release
  ~Singleton()
  {
    delete _instance;
  }
};

// Initialization of the internal static variable _instance,
// according to C ++ syntax
Singleton* Singleton::_instance = nullptr;

void main()
{
  // Demonstration of using the Singleton pattern.
  // Create a single instance of the Singleton class
  Singleton* obj1 = Singleton::Instance();

  if (obj1 != nullptr)
  {
    obj1->Set(255);
    obj1->Print(); // d = 255
  }

  // Attempting to create another instance of the Singleton class
  Singleton* obj2 = Singleton::Instance(); // obj2 = nullptr

  if (obj2 != nullptr)
  {
    obj2->Set(300);
    obj2->Print();
  }
}

The result of the program

d = 255

 

4. The advantages of using Singleton pattern

Using the Singleton pattern provides the following benefits:

  • control within the class over the creation of a single instance (a certain number of instances). This makes it easier to write client-side validation code;
  • reducing the number of names used in the program. This is because the Singleton pattern replaces storing unique instances of classes in global variables;
  • the pattern allows inheritance (extension). This allows you to modify methods (operations) in subclasses. In these subclasses, you can configure how the base class works and get the desired instance at runtime;
  • allows a variable number of instances. The Singleton pattern approach can be used to change the limit on the number of instances (for example, 3 instances) of classes. To do this, you need to reprogram the Instance() method accordingly.

 

5. An example of using the Singleton pattern to create no more than three instances of classes

The Singleton pattern can be modified to restrict the creation of any number of instances of the class. The example demonstrates the Singleton pattern approach for creating no more than three instances of a class named Singleton3.

Instances are counted using two internal variables:

  • array _instance of 3 pointers to type Singleton3*;
  • the count variable is the number of instances created.

The C++ code is as follows:

#include <iostream>
using namespace std;

// Implementation of pattern Singleton in C++.
// Creating up to three instances of the Singleton class
class Singleton3
{
private:
  // Static internal variable that stores an array of instances of a class.
  static Singleton3* _instance[3]; // declaring 3 pointers to Singleton3
  static int count; // the current number of instances created

  // Some class data, there can be any data here
  int d;

protected:
  // A protected class constructor in order to:
  // - it was impossible to create an instance of the class using the new operation from the client code;
  // - it was possible to inherit this class from other subclasses.
  Singleton3()
  {
    d = 0;
  }

public:
  // A static method that returns an instance of the Singleton class.
  // The method checks if an instance has been created
  static Singleton3* Instance()
  {
    if (count < 3)
    {
      _instance[count] = (Singleton3*) new Singleton3();
      count++;
      return (Singleton3*)(_instance[count - 1]);
    }
    else
    {
      return nullptr;
    }
  }

  // Methods (operations) for accessing internal data of a class (d fields)
  int Get()
  {
    return d;
  }

  void Set(int _d)
  {
    d = _d;
  }

  // Method that outputs the value of the internal field d
  void Print()
  {
    cout << "d = " << d << endl;
  }

  // Class destructor - correct memory release
  ~Singleton3()
  {
    if (count > 0)
    {
      for (int i = 0; i < count; i++)
        delete _instance[i];
      delete[] _instance;
    }
  }
};

// Initialization of the internal static variable _instance
// and a static counter count.
Singleton3* Singleton3::_instance[] = { nullptr,nullptr,nullptr };
int Singleton3::count = 0;

void main()
{
  // Demonstration of using the Singleton pattern
  // Create three instances of Singleton class
  Singleton3* objArray[3];

  for (int i = 0; i < 3; i++)
  {
    // Create an instance
    objArray[i] = Singleton3::Instance();

    if (objArray[i] != nullptr)
    {
      objArray[i]->Set(i * 5);
      objArray[i]->Print();
    }
  }

  // Attempt to create the fourth object
  Singleton3* obj4 = Singleton3::Instance();
  if (obj4 != nullptr)
  {
    obj4->Set(25);
    obj4->Print();
  }
  else
    cout << "Error of creating obj4." << endl;
}

The result of the program

d = 0
d = 5
d = 10
Error of creating obj4.

 


Related topics