目录
单例模式(Singleton Pattern , 也称单件模式;属于创建型模式)是23个设计模式中最简单的设计模式之一。它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 简单说来,单例模式的作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)
定义
- 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
实现单例模式的思路是:
- 一个类只有一个实例对象。在C++中一般是将构造函数、拷贝构造函数以及赋值操作符函数声明为private级别,从而阻止用户实例化一个类。那么,如何才能获得该类的对象呢?需要类提供一个 public static的方法,通过该方法获得这个类唯一的一个实例化对象。
- 当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例,并将实例的引用赋予该类的静态私有变量;
- 同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。代码如下:
饿汉式(线程安全):类加载的时候对象就已经存在,在类创建的同时就已经创建好一个静态的对象供系统使用,不管后面用不用这个类。所以没有延时加载功能
class Singleton
{
public:
static Singleton* GetInstance();
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* singleton;
};
Singleton* Singleton::singleton = new Singleton();
Singleton* Singleton::GetInstance()
{
return singleton;
}
int main()
{
Singleton* ct1 = Singleton::GetInstance();
delete ct1;
system("pause");
return 0;
}
饿汉式是单例实现最简单的方式,因此它的优点也是实现简单,同样缺点也非常明显,做不到延迟加载。当单例类调用不是特别频繁且存在大量资源占用时,使用饿汉模式会导致单例类在程序初始时就被实例化,浪费系统资源。
懒汉式(延迟初始化,线程不安全):该对象的实例只有在被需要的时候才进行创建,而不是一开始声明的时候就创建。
class Singleton
{
public:
static Singleton* GetInstance();
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* singleton;
};
Singleton* Singleton::singleton = nullptr;
Singleton* Singleton::GetInstance()
{
if (singleton == nullptr)
singleton = new Singleton();
return singleton;
}
int main()
{
Singleton* ct1 = Singleton::GetInstance();
delete ct1;
system("pause");
return 0;
}
优点:
- 避免了饿汉式的那种在没有用到的情况下就创建事例,资源利用率高,不执行 GetInstance()方就不会被创建实例,可以执行该类的其他静态方法。
缺点:
- 懒汉式在单个线程中没有问题,然而这种写法并不是线程安全的,因为如果两个线程同时到达 singleton == nullptr 的判断时,就会各自创建一个实例,这样就得到了两个实例,这就违反了单例模式的规约。
双重锁的形式 (线程安全):
#include <Windows.h>
using namespace std;
class Singleton
{
private:
static Singleton* instance;
//临界区,防止多线程产生多个实例
static CRITICAL_SECTION m_Sec;
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static CRITICAL_SECTION* getLock()
{
return &m_Sec;
}
static Singleton* GetInstance()
{
//双重锁定
if (instance == nullptr)
{
EnterCriticalSection(&m_Sec); //进入临界区
if (instance == nullptr)
instance = new Singleton();
LeaveCriticalSection(&m_Sec); //离开临界区
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
CRITICAL_SECTION Singleton::m_Sec = CRITICAL_SECTION();
int main()
{
//初始化临界区
InitializeCriticalSection(Singleton::getLock());
Singleton* singleton1 = Singleton::GetInstance();
Singleton* singleton2 = Singleton::GetInstance();
//删除临界区
DeleteCriticalSection(Singleton::getLock());
if (singleton1 == singleton2)
{
std::cout << "两个对象是相同的实例。" << std::endl;
}
delete singleton1;
system("pause");
return 0;
}
优点
- 减少内存开支,避免反复创建和销毁。
- 只有一个实例,减少系统性能开销,特别是产生一个对象需要比较多的资源时。
- 避免对资源的多重占用,例如写文件动作,只有一个实例存在可以避免对同一个资源文件的同时写操作。
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问。
缺点
单例模式一般无接口,扩展困难。
对测试不利,单例类未完成时不能进行测试。
与单一职责原则有冲突,单例模式把要单例的业务逻辑融合在一个类中。
适用场景
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象。
- 以及其他我没用过的所有要求只有一个对象的场景。先紫萼
先写这么多把,第一次学,水平有限