什么是单例模式
单例模式是常见的一种软件设计模式,单例对象的类只能实例化一个对象。
该类负责创建对象,同时保证只能创建一个对象。并提供一个访问它的全局访问点,该实例被所有程序模块共享。
一般应用与工具类的实现或者消耗资源的场景。
特点:
- 类构造函数私有
- 持有自己类的引用
- 对外获取实例的静态方法
饿汉模式
实例在初始化已经建好,不管你用不用,先建好再说。
优点:没有线程安全的问题
缺点:浪费内存空间。
class singleton
{
public:
//对外提供获取实例的静态方法
static singleton* Getinstance()
{
return &m_instance;
}
//构造函数私有
private:
singleton(){};
//C++98 防拷贝
//singleton(singleton const&);
//singleton& operator=(singleton const&);
//C++11 防拷贝
singleton(singleton const&) = delete;
singleton& operator=(singleton const&) = delete;
static singleton m_instance;
};
//程序入口地址之前就完成对单例对象的初始化
singleton singleton::m_instance;
懒汉模式
用的时候才去检查有没有创建实例,如果有则返回,没有则新建。
template <typename T>
class Singleton {
static T* inst;
public:
static T* GetInstance() {
if (inst == NULL) {
inst = new T();
}
return inst;
}
};
示例中的INSTANCE对象一开始是空的,在调用getInstance方法才会真正实例化。
上述代码有问题:
假如多个线程都调用了getinstance
方法,且都走到了判断为空那一步,可能同时成立。这样会导致多个线程同时创建instance
对象,即创建了多次,出现线程不安全的情况。
优点:没有内存空间浪费的问题。
缺点:控制不好不是单例。
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class singleton
{
public:
static singleton* Getinstance()
{
//双重检查锁。
//加锁之前检查一次是否为空,加锁之后再检查一次。
if (nullptr == m_instance)
{
m_mtx.lock();
if (nullptr == m_instance)
{
m_instance = new singleton();
}
m_mtx.unlock();
}
return m_instance;
}
//实现一个内嵌垃圾回收类
class CGarbo{
public:
~CGarbo(){
if (singleton::m_instance)
delete singleton::m_instance;
}
};
//定义一个静态成员变量,程序结束时,会自动调用它的析构函数从而释放单例对象。
static CGarbo Garbo;
private:
//构造函数私有
singleton(){};
//防拷贝
singleton(singleton const&);
singleton& operator=(singleton const&);
static singleton* m_instance;//单例对象指针
static mutex m_mtx;//互斥锁
};
singleton* singleton::m_instance = nullptr;
singleton::CGarbo Garbo;
mutex singleton::m_mtx;
void func(int n)
{
cout << singleton::Getinstance() << endl;
}
int mian()
{
thread t1(func, 10);
thread t2(func, 10);
t1.join();
t2.join();
cout << singleton::Getinstance() << endl;
cout << singleton::Getinstance() << endl;
}
加锁之前判断是否为空,可以确保instance
不为空的情况,不用加锁,直接返回。
单例模式的优缺点
优点
-
在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例
-
单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
提供了对唯一实例的受控访问。 -
由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
-
允许可变数目的实例。
-
避免对共享资源的多重占用。
缺点
-
不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
-
由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
-
单例类的职责过重,在一定程度上违背了“单一职责原则”。
-
滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
单例模式的适用场景
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象