单例模式
参考
https://leetcode-cn.com/leetbook/read/cpp-interview-highlights/ou1g1e/
https://zhuanlan.zhihu.com/p/62014096
应用场景
应用场景:
全局只有一个实例,比如打印机
实现方式
构造函数私有化,拷贝构造函数,拷贝赋值运算符delete;
需要注意多线程环境下 是否单例的情况(详见下文)。
实现一——线程不安全的原始单例
- 构造函数 拷贝构造 拷贝赋值 私有化
//构造函数拷贝构造private化
Singleton()=default;
Singleton(const Singleton & ) = delete;
Singleton & operator=(const Singleton &)= delete;//拷贝赋值
=delete
的运用
class Singleton{
private:
//构造函数拷贝构造private化
Singleton()=default;
Singleton(const Singleton & ) = delete;
Singleton & operator=(const Singleton &)= delete;//拷贝赋值
static Singleton * instance;
public:
static Singleton * get_instance(){
if(instance==nullptr)
instance = new Singleton();
return instance;
}
};
Singleton* Singleton::instance = nullptr;//static类成员必须类外定义
测试代码 线程不安全的原始单例
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <vector>
#include <string>
using namespace std;
class Singleton{
private:
//构造函数拷贝构造private化
Singleton()=default;
Singleton(const Singleton & ) = delete;
Singleton & operator=(const Singleton &)= delete;//拷贝赋值
static Singleton * instance;
public:
static Singleton * get_instance(){
if(instance==nullptr)
instance = new Singleton();
return instance;
}
};
Singleton* Singleton::instance = nullptr;//static类成员必须类外定义
void predict(const int &a ){
auto ptr = Singleton::get_instance();
printf("ptr %x\tthread id %x\n",ptr,std::this_thread::get_id());
}
int main(){
thread t1(predict,9);
thread t2(predict,4);
thread t3(predict,90);
thread t4(predict,0);
t1.join();
t2.join();
t3.join();
t4.join();
cout << "multithread ended\n";
return 0;
}
可能的输出 显示多个地址非单例
ptr 40000b60 thread id 4b7e0700
ptr 4c000b60 thread id 516c0700
ptr 44000b60 thread id 50eb0700
ptr 3c000b60 thr
multithread ended
实现二——多线程加锁单例 饿汉模式
初始化静态枷锁
class Singleton{
private:
//构造函数拷贝构造private化
Singleton()=default;
Singleton(const Singleton & ) = delete;
Singleton & operator=(const Singleton &)= delete;//拷贝赋值
static Singleton * instance;
public:
static Singleton * get_instance(){
//if(instance==nullptr)
// instance = new Singleton();
return instance;
}
};
Singleton* Singleton::instance = new Singleton();//static类成员必须类外定义
实现三——懒汉模式加锁
无论加锁的方法 用mutex::lock
和mutex::unlock
还是lock_guard
都需要注意位置 必须包住if(instance ==nullptr){...}
class Singleton{
private:
//构造函数拷贝构造private化
Singleton()=default;
Singleton(const Singleton & ) = delete;
Singleton & operator=(const Singleton &)= delete;//拷贝赋值
static Singleton * instance;
static std::mutex mtx;//锁
public:
static Singleton * get_instance(){
//mtx.lock();
std::lock_guard<std::mutex> lock_guard(mtx);
if(instance==nullptr){
//std::lock_guard<std::mutex> lock_guard(mtx);
instance = new Singleton();
}
// mtx.unlock();
return instance;
}
};
Singleton* Singleton::instance = nullptr;//static类成员必须类外定义
std::mutex Singleton::mtx;//static类成员类外定义
加锁位置在判断if(instance==nullptr)
之前;
如果两个线程同时发现instance==nullptr
都会再创建一个单例,所以无论哪种加锁方式,加锁都在null判断之前。
实现四——双锁模式
双锁模式(double-checked locking Pattern)也是懒汉模式的一种 在其基础上把get_instance
函数改为
static singleton * get_instance(){
//如果instance不为空就不用同步了
if(instance==nullptr){
mtx.lock();//同步
//同步线程中发现单例为空 于是new一个单例
if(instance==nullptr){
instance = new singleton();
}
mtx.unlock();
}
}
上例只给了双锁的简单实现,是否安全有效,甚至要结合volatile关键字,暂时不很理解。
实现五——最优雅的实现局部静态变量
https://www.zhihu.com/question/50533404/answer/156455984
局部静态变量已经是线程安全的了
class Singleton {
private:
//构造函数拷贝构造private化
Singleton()=default;
Singleton(const Singleton & ) = delete;
Singleton & operator=(const Singleton &)= delete;//拷贝赋值
//要优雅
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}
};
实现六——std::call_once
使用std::call_once
和std::once_flag
std::call_once
是全局使用一次,用后翻转flag
别的线程flag发现已经翻转了就不会再调用call_once里面的执行程序了。
static std::once_flag flag1;//静态
class Singleton{
private:
//构造函数拷贝构造private化
Singleton()=default;
Singleton(const Singleton & ) = delete;
Singleton & operator=(const Singleton &)= delete;//拷贝赋值
static unique_ptr<Singleton>instance;//定义unique_ptr 存放单例的指针
public:
//返回的是unique_ptr指向的对象的引用
static Singleton & get_instance(){
//flag1也可以定义在这里 必须作为静态变量,由于在静态函数中定义
std::call_once(flag1, [&](){ std::cout << "Simple example: called once\n";
instance.reset(new Singleton());//unique_ptr和shared_ptr一样可以用reset调整
});
return *instance;
}
};
unique_ptr<Singleton> Singleton::instance = nullptr;//static成员类内声明类外定义,类外定义时不带static关键字