Single Instance in C++
If you need to have one and only one object of a type in system AND you need to have global access to it reference
1. C++11 implementation of the Singleton design pattern
lazy-evaluated, correctly-destroyed, and thread-safe
class S
{
public:
static S& getInstance()
{
#if defined(__GNUC__) && (__GNUC__ > 3)
// it's OK, and thread save
// If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for // completion of the initialization.
#else
#error Add Critical Section for your platform
// 注意double checked locking pattern的问题. This will not work in C++03:
// https://stackoverflow.com/questions/367633/what-are-all-the-common-undefined-behaviours-that-a-c-programmer-should-know-a/367690#367690
#endif
static S instance; // Guaranteed to be destroyed. In C++11 standard: thread-safe
// Instantiated on first use. lazy-evaluated
return instance;
}
private:
S() {} // in C++ 11: S() = default;
// C++ 03
// ========
// Don't forget to declare these two. You want to make sure they
// are unacceptable otherwise you may accidentally get copies of
// your singleton appearing.
S(S const&); // Don't Implement
S& operator=(S const&); // Don't implement
// C++ 11
// =======
// 为了能够让程序员显式的禁用某个函数,C++11标准引入了一个新特性:deleted函数
// 程序员只需在函数声明后加上“=delete;”,就可将该函数禁用。
public:
S(S const&) = delete;
S& operator=(S const&) = delete;
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
};
another implementation
class Singleton {
public:
static Singleton& getInstance() {
if (instance == nullptr){
std::lock_guard<std::mutex> lock(mutex_);
if (instance == nullptr)
instance.reset(new Singleton());
}
return *instance;
}
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
private:
Singleton() {
cout << "constructor" << endl;
};
static std::mutex mutex_;
static std::shared_ptr<Singleton> instance;
};
std::mutex Singleton::mutex_;
std::shared_ptr<Singleton> Singleton::instance = nullptr;
2. 使用时需要注意的问题
C++中static变量的release顺序和allocate顺序相反:
class B
{
public:
static B& getInstance_Bglob;
{
static B instance_Bglob;
return instance_Bglob;;
}
~B()
{
A::getInstance_abc().doSomthing();
// The object abc is accessed from the destructor.
// Potential problem.
// You must guarantee that abc is destroyed after this object.
// To guarantee this you must make sure it is constructed first.
// To do this just access the object from the constructor.
}
B()
{
A::getInstance_abc();
// abc is now fully constructed.
// This means it was constructed before this object.
// This means it will be destroyed after this object.
// This means it is safe to use from the destructor.
}
};
3. 错误版本的Header
class SingleInstance {
public:
// 错误1,没有禁止copy constructer和opreator =
~SingleInstance();
static shared_ptr<SingleInstance> Get();
// 错误2,返回shared_ptr,导致其可以被清空,修改之类的
static shared_ptr<SingleInstance> GetInstance(const string ¶);
void Do() const;
bool IsInstanceSame(const string ¶) const;
private:
SingleInstance(const string ¶);
static shared_ptr<SingleInstance> single_instance_;
const string para_;
};
4. 错误版本的Implenmentation
shared_ptr<SingleInstance> SingleInstance::single_instance_ = NULL;
shared_ptr<SingleInstance> SingleInstance::Get() {
if (single_instance_ == NULL) {
LOG(ERROR) << "Must be initialized before using!";
throw std::runtime_error("Error runtime_error!");
}
return single_instance_;
}
shared_ptr<SingleInstance> SingleInstance::GetInstance(const string ¶) {
// 错误3,没有线程保护
if (!single_instance_ || !single_instance_->IsInstanceSame(para)) {
single_instance_.reset(new SingleInstance(para));
}
return single_instance_;
}
SingleInstance::SingleInstance(const string ¶) :
para_(para),
if (!(fs::exists(para))) {
LOG(ERROR) << "The file does not exist!";
throw std::invalid_argument("Error invalid_argument!");
}
... // some initializing work
}
SingleInstance::~SingleInstance() {
single_instance_.reset();
}
bool SingleInstance::IsInstanceSame(const string ¶) const {
return para_ == para;
}
void SingleInstance::Do() const {
... // do the main work
}