使用场景:确保只有一个类对象存在,避免此类被多次实例化。
比如软件中有个按钮,按下后弹出一个对话框,对话框中要显示一大堆数据,这些数据需要从一个文件中读取出来,用户选择一种后窗口消失,再次按下再次弹出。
很明显,每次弹窗都需要读取一次,效率极低,这个时候就需要写一个读取文件的类,将其设置为单例模式,只需读取一次,后续直接从内存中读取,可大大提高效率。
这里先写一个简单的“懒汉式”单例:
class LoadData
{
public:
LoadData();
~LoadData();
QString getData();
static LoadData* getInstance();
static LoadData* m_loader;
private:
QString m_data;
};
#include "LoadData.h"
LoadData* LoadData::m_loader = nullptr;
LoadData::LoadData()
{
//这里假装有一大堆读取数据的代码
m_data = "Hello world!";
}
LoadData::~LoadData()
{
}
LoadData* LoadData::getInstance()
{
if (m_loader ==nullptr)
{
m_loader = new LoadData();
}
return m_loader;
}
LoadData* ld = LoadData::getInstance();
for (int i = 0; i < 10;i++)
{
qDebug << ld->getData();
}
当第一次调用时走构造函数读取一次,后面的调用直接返回。所以懒汉式单例就是被调用时才将数据加载到内存中,如果对这个类使用频率不高时可使用这种方式。优点就是使用时才开销内存,缺点就是第一次调用耗时较长。
下面将其改为“恶汉式”单例:
初始化时直接new:
LoadData* LoadData::m_loader = new LoadData();
去掉判空操作:
LoadData* LoadData::getInstance()
{
/*if (m_loader ==nullptr)
{
m_loader = new LoadData();
}*/
return m_loader;
}
这样程序加载时自动将数据读取到内存中,调用时已经读过了,所以避免了第一次慢的问题。优点当然是调用时不费时,缺点是程序加载过程中就将数据加载到内存中,增加了内存的开销,如果程序中对此类调用频繁可使用此种方式。
到这里看似很简单,基本都实现了,其实还有很多问题:
1.如何防止别人直接构建类对象:
LoadData ld;//直接调用构造函数,还是每次都要读取,无法实现单例特性
解决方案:不让别人访问构造函数,将构造函数和类对象指针私有化:
private:
static LoadData* m_loader;
LoadData();
2.如何防止别人调用getinstance后解引用后拷贝给一个对象:
LoadData ld= *LoadData::getInstance();
解决方案:将拷贝构造函数私有化或者使用c++11特性设置为delete:
LoadData(const LoadData& loader) = delete;
3.如何防止调用两次getinstace后互相赋值问题:
LoadData* ld1 = LoadData::getInstance();
LoadData* ld2 = LoadData::getInstance();
*ld1 = *ld2;
解决方案将赋值操作符重载函数私有化或delete:
LoadData& operator=(const LoadData& loader) = delete;
4.如何防止多线程场景中被多次初始化?
解决方法:加双检锁
#include "mutex"
std::mutex rs_mutex;
LoadData* LoadData::m_loader = new LoadData();
LoadData* LoadData::getInstance()
{
if (m_loader==nullptr)
{
std::unique_lock myMutex(rs_mutex);
if (m_loader == nullptr)
{
m_loader = new LoadData();
}
}
return m_loader;
}
最后完整版代码:
#include
class LoadData
{
public:
QString getData();
static LoadData* getInstance();
LoadData(const LoadData& loader) = delete;
LoadData& operator=(const LoadData& loader) = delete;
private:
static LoadData* m_loader;
LoadData();
private:
QString m_data;
};
#include "LoadData.h"
#include "iostream"
#include "mutex"
std::mutex rs_mutex;
LoadData* LoadData::m_loader = new LoadData();
LoadData::LoadData()
{
//这里假装有一大堆读取数据的代码
m_data = "Hello world!";
}
LoadData* LoadData::getInstance()
{
if (m_loader==nullptr)
{
std::unique_lock myMutex(rs_mutex);
if (m_loader == nullptr)
{
m_loader = new LoadData();
}
}
return m_loader;
}
参考:慕课网轻松学习23中c++设计模式课程
参考文献:c++ 单例模式与多线程_c++ 单例模式 多线程_师范大学生的博客-CSDN博客