单例模式
用visual studio 2019测试
一、单例模式
1.定义
单例模式指在整个系统生命周期里,保证一个类只能产生一个实例,确保该类的唯一性。
2.分类
懒汉式:
指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)
饿汉式:
指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)
3.注意
为了保证单例,构造函数、析构函数、拷贝构造函数、赋值运算符重载函数均要定义成私有
普通的懒汉模式是非线程安全的,因为单例对象是在GetInstance()函数中实例化的,如果多个线程同时调用GetInstance()函数,则可能会实例化多个单例类,这就违背了单例模式的初衷了;
饿汉模式本身就是安全了,因为单例对象是跟随类的定义实例化的,所以一定是唯一的;
为了解决懒汉模式非线程安全的问题,需要在GetInstance()函数中实例化对象时加锁;
为什么不直接在单例类的析构函数中释放静态对象,而要弄一个内部类来释放静态对象?
只有在delete对象时候,才会调用析构函数,delete对象却是在析构函数中执行的,根本进不去析构函数,这就陷入了僵局;
二、懒汉式单例 ( 线程不安全 )
#include <iostream> // std::cout
#include <mutex> // std::mutex
#include<chrono>
#include<thread>
std::mutex _mutex;
class SingleInstance
{
public:
// 获取单例对象
static SingleInstance* GetInstance();
// 释放单例,进程退出时调用
static void deleteInstance();
// 打印单例地址
void Print();
private:
// 将其构造和析构成为私有的, 禁止外部构造和析构
SingleInstance();
~SingleInstance();
// 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
SingleInstance(const SingleInstance& signal);
const SingleInstance& operator=(const SingleInstance& signal);
private:
static SingleInstance* m_SingleInstance;
};
//初始化静态成员变量
SingleInstance* SingleInstance::m_SingleInstance = NULL;
SingleInstance* SingleInstance::GetInstance()
{
if (m_SingleInstance == NULL)
{
m_SingleInstance = new (std::nothrow) SingleInstance;
}
return m_SingleInstance;
}
void SingleInstance::deleteInstance()
{
if (m_SingleInstance)
{
delete m_SingleInstance;
m_SingleInstance = NULL;
}
}
void SingleInstance::Print()
{
std::cout << "我的实例内存地址是:" << this << std::endl;
}
SingleInstance::SingleInstance()
{
std::cout << "构造" << std::endl;
}
SingleInstance::~SingleInstance()
{
std::cout << "析构" << std::endl;
}
// 线程函数
void PrintHello(void* threadid)
{
// 主线程与子线程分离,两者相互不干涉,子线程结束同时子线程的资源自动回收
// 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
int tid = *((int*)threadid);
_mutex.lock();
std::cout << "Hi, 我是线程 ID:[" << tid << "]" << std::endl;
_mutex.unlock();
// 打印实例地址
SingleInstance::GetInstance()->Print();
}
#define NUM_THREADS 4// 线程个数
int main(void)
{
int indexes[NUM_THREADS] = { 0 }; // 用数组来保存i的值
int ret = 0;
int i = 0;
std::cout << "开始 ... " << std::endl;
for (int i = 0; i < NUM_THREADS; i++) {
_mutex.lock();
std::cout << "创建线程:" << i << std::endl;
_mutex.unlock();
indexes[i] = i; //先保存i的值
std::thread t(&PrintHello, (void*)&(indexes[i]));
t.detach();
}
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
// 手动释放单实例的资源
SingleInstance::deleteInstance();
std::cout << " 结束! " << std::endl;
return 0;
}
三、饿汉式单例
class Singleton
{
public:
// 获取单实例
static Singleton* GetInstance();
// 释放单实例delete
static void deleteInstance();
// 打印
void Print();
private:
// 私有的, 禁止外部构造和析构
Singleton();
~Singleton();
// 私有函数, 禁止外部拷贝和赋值
Singleton(const Singleton &signal);
const Singleton &operator=(const Singleton &signal);
private:
// 实例对象指针
static Singleton *g_pSingleton;
};
// 线程安全
Singleton* Singleton::g_pSingleton = new (std::nothrow) Singleton;
Singleton* Singleton::GetInstance()
{
return g_pSingleton;
}
void Singleton::deleteInstance()
{
if (g_pSingleton)
{
delete g_pSingleton;
g_pSingleton = NULL;
}
}
void Singleton::Print()
{
std::cout << "this:" << this << std::endl;
}
Singleton::Singleton()
{
}
Singleton::~Singleton()
{
}
懒汉式用到才会创建,所以线程少的时候合适
饿汉式反过来适合线程比较多,访问多的时候