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
2
  • 2
    Singletons are more of an anti-pattern, so I would recommend to not implement them at all. They are not that different from global variables. Commented Oct 28, 2015 at 18:58
  • 10
    While many agree that singletons, used unwisely, can be problematic, I don't think it is particularly useful here to criticize them -- it doesn't help the questioner solve a problem. Commented Aug 31, 2017 at 2:14

3 Answers 3

21

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?

Sign up to request clarification or add additional context in comments.

7 Comments

static variable of getInstance() does not work , I think
@IgorTandetnik, I tested your getInstance() in a multi-threaded tight loop and it works very well! But can you please explain what is going on? Why is 'Demo' only instantiated a single time? No checking for nullptr, what kind of magic is this?
@CarolineBeltran Funny you should say that. This feature of the C++ language is often referred to as "magic statics". The compiler automatically generates code that performs the necessary synchronization.
@IgorTandetnik, much clearer now, thank you. I currently pass a shared pointer to a single database pool instance in an Asio async app. It works very well but it is just plain messy looking. It would be so much cleaner to use a singleton but there is so much historical negative info regarding multi-threaded/singleton apps. On my tight loops (hundreds of millions of iterations), I experienced zero problems but I would like to know if YOU would trust a singleton db instance in your app, especially with the c++ 11 thread safe static initialization guarantee. Thanks again.
@CarolineBeltran I do use singleton instances in my project. None of them happen to manage a database connection, but I don't see why that would make a difference.
|
3

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; 
}

3 Comments

You do know that after executing Demo l = Demo::getInstance();, you will end up with a copy of your singleton, and thus you will not have a singleton anymore?
Make it Demo& l = Demo::getInstance(); (note the ampersand). Then mark Demo's copy constructor deleted, as in Demo(const Demo&) = delete;, so that your original example doesn't compile anymore. It's not much of a singleton if it can be copied, now is it?
Dont forget to delete the copy constructors to be safe! Demo(const Demo &) = delete;
1

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. "

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.