单例模式
参考
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判断之前。
实现四——最优雅的实现局部静态变量
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;
public:
//返回的是unique_ptr指向的对象的引用
static Singleton & get_instance(){
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关键字
|