3

I was reading the answers to the question. C++ Singleton design pattern

One of the answer suggested to use shared_ptr to guarantee the lifetime when multiple static objects access the singleton object. I noticed that shared_ptr here is constructed using new and is returned by value.

Is using new for shared_ptr construction atomic/thread_safe?

My second confusion is regarding RVO. I tried creating two singleton objects in my implementation.

The shared_ptr shows three _Uses and one _Weak. I expected _Uses count to be two. Optimization is full on Visual Studio 15.5.6. Is RVO not functioning here?

class Singleton
{
public:
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;

static std::shared_ptr<Singleton> instance()
{
    static std::shared_ptr<Singleton> s{new Singleton};
    return s;
}

    ~Singleton() {}
private:
    Singleton() {}
};

int main()
{
     std::shared_ptr<Singleton> obj1 = Singleton::instance();
     std::shared_ptr<Singleton> obj2 = Singleton::instance();
     return 0;
}
Sunshyn
  • 43
  • 5
  • The use of `shared_ptr` here is redundant. The static member variable `s` inside of the function `instance()` has a lifespan longer than any other shared_ptr created by calling Singleton::instance(). Thus why not just make it a normal object and return a reference. – Martin York Jul 27 '19 at 20:33
  • @MartinYork As stated in the [answer of this question](https://stackoverflow.com/a/40337728/7699037), the use of a `shared_ptr` helps to keep the singleton alive when multiple static object make use of it. – Mike van Dyke Jul 29 '19 at 08:13
  • 1
    @MikevanDyke As stated in my comment. It does not help at all. Remove the shared pointer (and replace it with a reference) at it will stay alive exactly as long. There is a reason that answer only got 12 votes. – Martin York Jul 29 '19 at 16:23
  • @MartinYork You're right, I was thinking, that if one singleton has a member that is reference to the another singleton, there might be a dangling reference if the second singleton is destroyed first. However, if the first singleton has a reference then the order of creation is given, and therefore also the order of destruction. – Mike van Dyke Jul 30 '19 at 09:16
  • 2
    @MikevanDyke Exactly. But there is one corner case. If you accesses the Singleton from a destructor of a static storage duration object after the singleton has itself been destroyed (this is a problem for the shared_ptr and reference version). But this problem can be solved. I wrote about it here: See the section: Destruction Problems: in https://stackoverflow.com/a/335746/14065 – Martin York Jul 30 '19 at 16:26
  • @MartinYork I tried creating static objects of two classes(A,B), both of which are accessing Singleton object. Class A initialized the singleton object first in its constructor and is destructed last( before B and Singleton). This is as expected. I explicitly called the destructor of A before returning from main, but that doesn't change the order of destruction. Why? Behaviour is same for static and non-static object, A is always destructed last. – Sunshyn Jul 31 '19 at 12:30
  • Singleton object is returned by reference this time. Explicit call of destructor seems to be of no effect. The destructor is called again at end of scope. – Sunshyn Jul 31 '19 at 12:38
  • @Sunshyn: `I explicitly called the destructor of A before returning from main`. You should **not** be explicitly calling the destructor. Doing this is only for very advanced techniques where you have talking full control over the lifespan. None of what we have discussed here is about that kind of control. Nor is it needed by Singletons. – Martin York Jul 31 '19 at 22:49
  • The order of destruction of static storage duration objects is the exact inverse of the order of construction. You can not change that order (or stop it from happening, calling the destructor does not remove it from some ordered list). The compiler has already planted the code to perform the operation. If you explicitly call the detructor your object has been destroyed, if the generated application code now calls the destructor again before you re-create that object you have undefined behavior. – Martin York Jul 31 '19 at 22:53

1 Answers1

2

Is using new for shared_ptr construction atomic/thread_safe?

Yes, the creation of the shared_ptr is thread safe because static initializations are thread-safe (see also this question)

The shared_ptr shows three _Uses and one _Weak. I expected _Uses count to be two. Optimization is full on Visual Studio 15.5.6. Is RVO not functioning here?

You have a use count of three, because the shared_ptr is declared static, which means when it is initialized, the object is created and thus the use count is increased by one (+2 for the two shared_ptr in the main). This use count is only decreased again, when the static shared_ptr is destroyed, and this happens when the programm terminates.

Mike van Dyke
  • 2,724
  • 3
  • 16
  • 31