单例模式基本定义:运行时只有一个实例,分为饿汉式(开始就实例化)和懒汉式(调用才实例化),下面实现懒汉式(因为我比较懒O(∩_∩)O~)
单例模式特点:
1.只有唯一全局访问点(单一接口)
2.只能实例化一次(静态变量)
3.不能赋值或者拷贝(把拷贝和赋值重载函数写在private里,或者设为delete)
额外实现:
线程安全(加锁)
防止内存泄漏(智能指针)
C++代码如下
#include <iostream>
#include <memory> //shared_ptr
#include <mutex>
#include <thread>
using namespace std;
class Singleton
{
private:
Singleton()
{
cout << "constructed!" << '\n';
}
static mutex mtx;
static shared_ptr<Singleton> single_instance;//智能指针
public:
~Singleton()
{
cout << "destructed!" << '\n';
}
Singleton(Singleton&) = delete;
Singleton* operator=(const Singleton&) = delete;//禁止拷贝与构造
static shared_ptr <Singleton> getInstance()
{
if (single_instance == nullptr)//第一层检验
{
cout << "a"<<" ";
mtx.lock();//上锁
if (single_instance == nullptr)//第二层检验
{
single_instance = shared_ptr<Singleton>(new Singleton);
}
mtx.unlock();//解锁
}
return single_instance;
}
};
shared_ptr<Singleton> Singleton::single_instance = nullptr;//初始化
mutex Singleton::mtx;
void Test()
{
for (int i = 0; i < 100; i++)
{
shared_ptr<Singleton> ptr = Singleton::getInstance();
// cout << ptr.use_count()<<" ";
}
}
int main()
{
thread th1(Test);
thread th2(Test);
th1.join();
th2.join();
return 0;
}
运行结果1
a constructed!
a destructed!
运行结果2
a a constructed!
destructed!
从运行结果可以看到,两个线程同时尝试初始化Singleton时,只有一个可以成功
结果二是两个线程开始都判断Singleton没有实例化,但是其中一个加了锁并成功实例化,另一个就不能实例化
Q:为什么要用双重判断呢?
第一层判断是为了防止多次加锁,浪费资源
第二层判断是为了防止重复创建实例
Q:单例模式的应用和缺陷
应用场景:只需要一个实例化对象,比如日志系统,GUI组件(整个应用程序只需要一个GUI组件),并且该对象可能会被频繁地销毁和创建
缺陷:全局对象所有子系统都可以调用,增大了耦合性,如果某个系统改变了单例的实例并引发崩溃,很难去维护
其他:单例模式还可以用局部静态变量实现,更加简单,这里不赘述啦