单例模式
- 定义
保证⼀个类仅有⼀个实例,并提供⼀个该实例的全局访问点。
下面从几个单例模式的实现版本和大家探讨单例模式的实现
版本一
// 内存栈区
// 内存堆区
// 常数区
// 静态区 系统释放
// ⼆进制代码区
class Singleton {
public:
static Singleton * GetInstance() {
if (_instance == nullptr) {
_instance = new Singleton();
}
return _instance;
}
private:
Singleton(){}//构造
Singleton(const Singleton &clone){} //拷⻉构造
Singleton& operator=(const Singleton&) {}
static Singleton * _instance;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
存在的问题:
- 这种写法在单线程模式下就会有内存泄漏。因为_instance在静态区,静态区由内存释放
而new的对象在堆区 - 线程安全问题:当多线程获取单例时有可能引发竞争。因为New的时候没加锁
版本二
class Singleton2
{
private:
static Singleton2 * GetInstance() {
if (_instance == nullptr) {
_instance = new Singleton2();
atexit(Destructor);
}
return _instance;
}
public:
static void Destructor() {
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
}
}
Singleton2(/* args */);
Singleton2(const Singleton2 &cpy);
static Singleton2 * _instance;
};
存在的问题:
这样实现可以避免单线程模式下的内存泄漏。但如果是多线程下,此时还有会线程安全的问题。
版本三、四的都是懒汉式,即需要的时候才实例化单例。
版本三——加锁
#include <mutex>
class Singleton { // 懒汉模式 lazy load
public:
static Singleton * GetInstance() {
// std::lock_guard<std:mutex> lock(_mutex) 在这加锁性能不好,锁的粒度大,加锁的话会涉及线程切换
if (_instance ==nullptr) {
std::lock_guard<std::mutex> lock(_mutex); //自旋锁 不用Unlock
_instance = new Singleton2(); // new 个步骤:1分配内存,2调用构造函数,3赋值操作
atexit(Destructor);
}
return _instance;
}
private:
static void Destructor() {
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
}
}
Singleton(){}
Singleton(const Singleton &clone){} //拷贝构造
Singleton& operator=(const Singleton&){}
static Singleton * _instance;
static std::mutex _mutex;
}
Singleton* Singleton::_instance = nullptr; //静态成员初始化
存在的问题:
- 这个版本其实是存在问题的,在New的时候:1分配内存,2调用构造函数,3赋值操作,多线程环境下cpu进行重排 1 3 2 这时候会有线程问题
版本四——使用cpp11的原子类
#include <mutex>
#include <atomic>
class Singleton {
public:
static Singleton * GetInstance() {
Singleton *tmp = _instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire); // 获取内存屏障,防止reorder
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(_mutex);
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release); // 释放内存屏障
_instance.store(tmp, std::memory_order_release);
atexit(Destructor);
}
}
return tmp;
}
private:
static void Destructor() {
Singleton *tmp = _instance.load(std::memory_order_relaxed);
if (tmp != nullptr) {
delete tmp;
}
}
Singleton(){}
Singleton(const Singleton &clone){} //拷贝构造
Singleton& operator=(const Singleton&){}
static std::atomic<Singleton *> _instance;
static std::mutex _mutex;
}
//静态成员初始化
std::atomic<Singleton *> Singleton::_instance;
std::mutex Singleton::_mutex;
需要注意的点就是在编译的时候加上c++11即可:g++ Singleton.cpp -o singleton -std=c++11
版本五——静态局部变量(c++11)
在c++11的static自带线程安全,如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。如果不是c++11的话还是会有线程安全问题哦~
代码如下:
template<typename T>
class Singleton {
public:
static T& GetInstance(){
static Singleton instance;
return instance;
}
virtual ~Singleton(){}
protected:
Singleton(){}
Singleton(const Singleton&) {}
Singleton& operator = (const Singleton&) {}
}
class DesignPattern:public Singleton<DesignPattern> {
friend class Singleto<DesignPattern>; // friend可以让Singleton<DesignPattern>可以访问到自己私有的成员
private:
DesignPattern(){}
DesignPattern(const DesignPattern &clone){} //拷贝构造
DesignPattern& operator=(const DesignPattern&){}
}