c++懒汉式单例模式(Singleton)多种实现方式及最优比较
前言
关于C++懒汉式单例模式的写法,大家都很熟悉。早期的设计模式中有代码示例。比如:
class Singleton {
private: static Singleton *instance;
public: static Singleton *getInstance() {
if (NULL == instance)
instance = new Singleton();
return instance;
}
};
它的缺点:线程不安全,指针资源没有释放。
自从C++11推出后,单例模式有了更优秀的写法,下面来介绍下。
使用 std::call_once 实现
#include <iostream>
#include <mutex>
#include <memory>
class Singleton {
private:
Singleton() { std::cout << "Singleton constructed." << std::endl; }
static std::once_flag initInstanceFlag;
static std::unique_ptr<Singleton> instance;
// 删除拷贝构造函数和赋值操作符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
std::call_once(initInstanceFlag, []() {
instance.reset(new Singleton());
});
return *instance;
}
void someMethod() {
std::cout << "Method of the singleton" << std::endl;
}
~Singleton() {
std::cout << "Singleton destructed." << std::endl;
}
};
std::once_flag Singleton::initInstanceFlag;
std::unique_ptr<Singleton> Singleton::instance;
// 使用示例
void threadFunction() {
Singleton& singleton = Singleton::getInstance();
singleton.someMethod();
}
int main() {
std::thread t1(threadFunction);
std::thread t2(threadFunction);
t1.join();
t2.join();
return 0;
}
优点:线程安全、内存安全。显式控制初始化过程,适合需要延迟初始化的场景。
使用局部静态变量实现(C++11及以后)
#include <iostream>
#include <memory>
class Singleton {
private:
Singleton() { std::cout << "Singleton constructed." << std::endl; }
// 关闭拷贝构造函数、右值拷贝构造函数和赋值操作符
Singleton(const Singleton&) = delete;
Singleton(const Singleton &&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
void someMethod() {
std::cout << "Method of the singleton" << std::endl;
}
~Singleton() {
std::cout << "Singleton destructed." << std::endl;
}
};
// 使用示例
void threadFunction() {
Singleton& singleton = Singleton::getInstance();
singleton.someMethod();
}
int main() {
std::thread t1(threadFunction);
std::thread t2(threadFunction);
t1.join();
t2.join();
return 0;
}
优点:线程安全。代码最简洁,由C++11标准保证线程安全,适合大多数场景。
缺点:适用于不复杂的工程。因为如果静态类之间有依赖,可能会导致C++的一些未定义的行为。
Meyers 的版本
Scott Meyers 是 Effective C++系列的作者,他最早提供了简洁版本的 Singletion 模型。根据他提供的模型,可以写出线程安全又简单的单例模式。代码如下:
#include <stdio.h>
class singleton {
static singleton &instance() {
static singleton instance;
return instance;
} // instance
singleton(const singleton &) = delete;
singleton & operator = (const singleton &) = delete;
private:
singleton() {}
~singleton() {}
public:
void out(){ printf("out\n"); }
}; // struct singleton
int main() {
singleton::instance().out();
return 0;
}
缺点:单一的实例总是在 main() 开始之前被初始化的,该实现无法做到 lazyinit。
优化版本:
template<typename T>
class singleton {
public:
static T &instance();
singleton(const singleton &) = delete;
singleton &operator=(const singleton) = delete;
protected:
singleton() = default;
};
template<typename T>
inline T &singleton<T>::instance() {
static const std::unique_ptr<T> instance{new T{token{}}};
return *instance;
}
优点:线程安全、内存安全。
鸿蒙单例实现
线程安全、内存安全、双重检测、延迟加载、支持lazyinit、实现懒汉式单例模板。当前在商业(鸿蒙手机操作系统)使用中,代码可靠。
使用说明,请点这里。
下面只展示DelayedSingleton实现示例。完整代码以及其它几种单例实现,请点这里。
补充说明:对于懒汉式单例模式双重检测,有的人嫌代码麻烦,将构造函数私有化来实现。这个大家自己去评价。其实也没几行代码。写上双重检测,逻辑上也是提醒大家代码实现的风险点。不算优化问题。论可靠,还是相信鸿蒙吧。
template<typename T>
class DelayedSingleton : public NoCopyable {
public:
static std::shared_ptr<T> GetInstance();
static void DestroyInstance();
private:
static std::shared_ptr<T> instance_; instance.
static std::mutex mutex_;
};
template<typename T>
std::shared_ptr<T> DelayedSingleton<T>::instance_ = nullptr;
template<typename T>
std::mutex DelayedSingleton<T>::mutex_;
template<typename T>
std::shared_ptr<T> DelayedSingleton<T>::GetInstance()
{
if (instance_ == nullptr) {
std::lock_guard<std::mutex> lock(mutex_);
if (instance_ == nullptr) {
std::shared_ptr<T> temp(new (std::nothrow) T);
instance_ = temp;
}
}
return instance_;
}
template<typename T>
void DelayedSingleton<T>::DestroyInstance()
{
std::lock_guard<std::mutex> lock(mutex_);
if (instance_ != nullptr) {
instance_.reset();
instance_ = nullptr;
}
}