Currently I'm using this approach:
class Singleton {
public:
static Singleton &getInstance() {
static Singleton *instance = new Singleton();
return *instance;
}
void getData();
private:
Singleton() {}
};
In this way I can use a method from Singleton writing:
Singleton::getInstance.getData();
And this seems the right way reading a lot of tutorials for C++11. But reading through cocos Director singleton code (also FileUtils etc..), I have seen that Cocos uses this other approach:
class Singleton {
public:
static Singleton *getInstance() {
instance = new Singleton();
return instance;
}
void getData();
private:
Singleton() {}
static Singleton *instance;
};
With this approach I have to write:
Singleton::getInstance->getData();
Because of the pointer *getInstance instead of reference &getInstance.
I think the difference is big, but I don't know if one way is correct and the other don't.
Please help me to sorting out this concept.
In my view, the best singleton is presented as a value type, with a hidden singleton implementation.
This means that you can pass the singleton around like any other object.
This in turn means that if you change your mind later, and the singleton actually needs to be a normal object, you don't need to change any code.
It also means that your singleton object can participate in ADL and tag-dispatching.
eg:
#include <iostream>
// only this goes in the header file
struct my_thing
{
// public interface
int do_or_get_something(int arg);
private:
struct impl;
static impl& get_impl();
};
// this goes in the cpp file
struct my_thing::impl
{
int do_or_get_something(int arg)
{
// for demo purposes
return counter += arg;
}
int counter = 0; // for demo purposes
};
my_thing::impl& my_thing::get_impl()
{
// create first time code flows over it - c++ standard
// thread safe in c++11 - guarantee
static impl instance {};
return instance;
}
// implement public interface in terms of private impl
int my_thing::do_or_get_something(int arg)
{
return get_impl().do_or_get_something(arg);
}
// test the concept
int main()
{
auto one_thing = my_thing();
std::cout << one_thing.do_or_get_something(5) << std::endl;
std::cout << one_thing.do_or_get_something(5) << std::endl;
auto another_thing_but_really_same = my_thing();
std::cout << another_thing_but_really_same.do_or_get_something(5) << std::endl;
std::cout << my_thing().do_or_get_something(5) << std::endl;
std::cout << one_thing.do_or_get_something(5) << std::endl;
}
expected output:
5
10
15
20
25