vladon
vladon

Reputation: 8401

Singleton: how can destructor be called twice?

I asked a question about singleton implementation a few minutes ago, I've got very good answer from @LightnessRacesinOrbit.

But I cannot understand why in the next example if I instantiate Singleton in variable inst its destructor called twice?

#include <iostream>

class Singleton
{
public:
    ~Singleton()  { std::cout << "destruction!\n"; }

    static Singleton& getInstance() 
    {
        static Singleton instance;
        return instance;
    }

    void foo() { std::cout << "foo!\n"; }

private:
    Singleton() { std::cout << "construction!\n"; }
};

int main()
{
    Singleton inst = Singleton::getInstance();
    inst.foo();
}

Output:

construction!
foo!
destruction!
destruction!

Live demo

To be more correct, I understand why it is called twice. But I cannot understand how it can be called twice if after first destructor the instance of the class was destroyed? Why there is no exception?

Or it was not destroyed? Why?

Upvotes: 7

Views: 1143

Answers (4)

Cory Kramer
Cory Kramer

Reputation: 117866

This line

Singleton inst = Singleton::getInstance();

Should be

Singleton& inst = Singleton::getInstance();

Then you would only see one destructor call.

The way it is written, Singleton::getInstance() returns a reference, but then copies it to inst. So both the Singleton returned from your function and the copy are destroyed. You never saw the copy get constructed because the default constructor was not used, the copy constructor was.

In the second method, the reference is returned, then you just have inst be a reference to that Singleton instead of making a copy.

As other's have mentioned, you can make the class non-copyable and non-movable to prevent this

Singleton(Singleton const&) = delete;             // Copy construct
Singleton(Singleton&&) = delete;                  // Move construct
Singleton& operator=(Singleton const&) = delete;  // Copy assign
Singleton& operator=(Singleton &&) = delete;      // Move assign

Upvotes: 18

user2249683
user2249683

Reputation:

You may hide the singleton in a regular class not exhibiting its static nature:

#include <iostream>
#include <string>

class Singleton // A bad name for a specific class, you should not generalize
{
    private:
    struct Context {
        std::string name;
        Context()
        :   name("Hello")
        {}
    };

    private:
    static Context& context() { static Context result; return result; }

    public:
    const std::string& name() const { return context().name; }
};

int main()
{
    Singleton a;
    std::cout << a.name() << '\n';
}

Upvotes: 2

Daniel Frey
Daniel Frey

Reputation: 56863

The line

Singleton inst = Singleton::getInstance();

copies your instance with the auto-generated copy-constructor. To prevent this from happening, add

Singleton( const Singleton& ) = delete;

to your class to prevent those accidential copies. To make sure that even more obscure mistakes are caught, also add

void operator=( const Singleton& ) = delete;

as well. You don't have to explicitly delete move construction or assignment as the compiler will not generate them with the other (deleted) members declared.

Upvotes: 8

anon
anon

Reputation:

Try adding a public copy-constructor:

Singleton(const Singleton&) { std::cout << "copy construction!\n"; }

Your output will become:

construction!
copy construction!
foo!
destruction!
destruction!

There are two "singletons" alive because the one from getInstance() got copied. To avoid inadvertently copying singletons, you should delete the copy constructor and assignment operators:

Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

Upvotes: 3

Related Questions