Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singleton class implementation using shared_ptr

Tags:

c++11

#include <iostream>
#include <memory>

using namespace std;

class Demo {
    static shared_ptr<Demo> d;
    Demo(){}
public:    
    static shared_ptr<Demo> getInstance(){
        if(!d)
        d.reset(new Demo);
        return d;
    }
    ~Demo(){
        cout << "Object Destroyed " << endl;
    }

};

//    shared_ptr<Demo> Demo::d(new Demo); // private ctor is accepted 

shared_ptr<Demo> Demo::d;

int main()
{
    shared_ptr<Demo> d(Demo::getInstance());
    cout << d.use_count() << endl;

   return 0;
}
  1. is this the correct way to implement the singleton class using shared_ptr
  2. please see above commented line to initialize the static shared_ptr how come we can create an object here to initialize shared_ptr with a private construct
like image 214
Jagan Arikuti Avatar asked Oct 27 '15 21:10

Jagan Arikuti


3 Answers

This is not thread-safe: two threads calling getInstance would cause a data race. A common approach is to use a function-scope static variable:

static shared_ptr<Demo> getInstance(){
  static shared_ptr<Demo> d(new Demo);
  return d;
}

Such a variable is guaranteed to be initialized exactly once, when control passes over its definition for the first time, and in a thread-safe manner.

At this point though, it's not at all clear why you would want to use shared_ptr. You could just as well do

static Demo& getInstance(){
  static Demo d;
  return d;
}

This is a textbook implementation of a singleton (well, one of).


Re: initialize with a private constructor. I'm not sure I understand the nature of your confusion. Are you asking why Demo::getInstance can use private constructor of Demo? Well, because it's a member of Demo, and members of a class can access private members of that class. Are you asking why Demo::getInstance can call shared_ptr<Demo>::reset() passing a Demo* pointer? Well, because reset() is a public member function of shared_ptr, taking a pointer as a parameter. Which part of this process do you find controversial?

like image 142
Igor Tandetnik Avatar answered Oct 21 '22 13:10

Igor Tandetnik


Some comments from 1 to help with the discussion. Static variable will be destroyed uppon exit of the application, so we don't need to use the smart pointer at this stage as mentioned above.

"If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once).

Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison. (since C++11)

The destructor for a block-scope static variable is called at program exit, but only if the initialization took place successfully. "

like image 38
Mickael T Avatar answered Oct 21 '22 12:10

Mickael T


My 2nd Question above is that how come private constructor called out side of class while instantiating the static member

//    shared_ptr<Demo> Demo::d(new Demo); // private ctor is accepted 

I think return local static wont work , see below example object destroyed twice

#include <iostream>

using namespace std;

class Demo {
public:
    static Demo & getInstance(){
        static Demo d;
        return d;
    }
    ~Demo(){
        cout << "Demo destroyed" << endl;
    }
};

void fun(){
    Demo l = Demo::getInstance();

}
int main()
{
    fun();
   cout << "Hello World" << endl; 
}
like image 3
Jagan Arikuti Avatar answered Oct 21 '22 12:10

Jagan Arikuti