单例模式的作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)。
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
实现单例模式的思路是:
- 一个类只有一个实例对象。在C++中一般是将构造函数、拷贝构造函数以及赋值操作符函数声明为private级别,从而阻止用户实例化一个类。那么,如何才能获得该类的对象呢?需要类提供一个 public static的方法,通过该方法获得这个类唯一的一个实例化对象。
- 当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例,并将实例的引用赋予该类的静态私有变量;
- 同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。
饿汉模式
饿汉式(线程安全):类加载的时候对象就已经存在,在类创建的同时就已经创建好一个静态的对象供系统使用,不管后面用不用这个类。(静态初始化)
#include <iostream>
using namespace std;
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();
Singleton* ct2 = Singleton::GetInstance();
if(ct1 == ct2)
cout<<"两个对象是相同的实例"<<endl;
delete ct1;
return 0;
}
饿汉式是单例实现最简单的方式,因此它的优点也是实现简单,同样缺点也非常明显,做不到延迟加载。当单例类调用不是特别频繁且存在大量资源占用时,使用饿汉模式会导致单例类在程序初始时就被实例化,浪费系统资源。
懒汉模式
懒汉式(延迟初始化,线程不安全):该对象的实例只有在被需要的时候才进行创建,而不是一开始声明的时候就创建。
#include <iostream>
using namespace std;
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();
Singleton* ct2 = Singleton::GetInstance();
if(ct1 == ct2)
cout<<"两个对象是相同的实例"<<endl;
delete ct1;
return 0;
}
优点:
- 避免了饿汉式的那种在没有用到的情况下就创建事例,资源利用率高,不执行 GetInstance()方就不会被创建实例,可以执行该类的其他静态方法。
缺点:
- 懒汉式在单个线程中没有问题,然而这种写法并不是线程安全的,因为如果两个线程同时到达 singleton == nullptr 的判断时,就会各自创建一个实例,这样就得到了两个实例,这就违反了单例模式的规约。
懒汉式加锁,双重锁的形式 (线程安全):
#include <iostream>
#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;
delete singleton2;
system("pause");
return 0;
}