Reputation: 8401
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!
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
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
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
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
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