为什么要使用单例?
单例设计模式(Singleton Design Pattern)理解起来非常简单。一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。
实战案例一:处理资源访问冲突
实战案例二:表示全局唯一类
从业务概念上,如果有些数据在系统中只应保存一份,那就比较适合设计为单例类。
配置信息类。在系统中,我们只有一个配置文件,当配置文件被加载到内存之后,以对象的形式存在,也理所应当只有一份。
唯一递增 ID 号码生成器,如果程序中有两个对象,那就会存在生成重复 ID 的情况,所以,我们应该将 ID 生成器类设计为单例。
实现方法
1、懒汉式
支持延迟加载。
如果遇到多线程,懒汉式是需要进行加锁的。如果这个单例类偶尔会被用到,那这种实现方式还可以接受。但是,如果频繁地用到,那频繁加锁、释放锁及并发度低等问题,会导致性能瓶颈。
2、饿汉式
不支持延迟加载,初始化时直接生成。在类加载的时候,instance 静态实例就已经创建并初始化好了,所以,instance 实例的创建过程是线程安全的。
3. 双重检测
这是用C++11改进的单例模式,其中使用了可变参数类来创建单例,使得单例模式可以创建任意个数的参数类型的单例类
通过使用模板函数,将单例模式封装到一个类里,如果使用的时候想让这个对象变成单例模式可以调用这个类包装一下,然后
#pragma once
template <typename T>
class Singleton
{
public:
template<typename... Args>
static T* Instance(Args&&... args)
{
if(m_pInstance==nullptr)
m_pInstance = new T(std::forward<Args>(args)...);
return m_pInstance;
}
static T* GetInstance()
{
if (m_pInstance == nullptr)
throw std::logic_error("the instance is not init, please initialize the instance first");
return m_pInstance;
}
static void DestroyInstance()
{
delete m_pInstance;
m_pInstance = nullptr;
}
private:
Singleton(void);
virtual ~Singleton(void);
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
private:
static T* m_pInstance;
};
template <class T> T* Singleton<T>::m_pInstance = nullptr;
#include <iostream>
#include <string>
class A
{
public:
A(const string &){cout << "lvalue" << endl;}
A(string && x){cout << "rvalue" << endl;}
void Func()
{cout << "test" << endl;}
};
int main()
{
string str = "nn";
singleton<A>::Instance(str);
singleton<A>::Instance(std::move(str));
singleton<A>::GetInstance->Func();
return 0;
}
上述单例模式是C++11以后。
其实平常使用单例模式的时候需要考虑的普通场景的话,简单用法就如下所示
头文件
#pragma once
#include <iostream>
using namespace std;
class Singleton
{
public:
//static Singleton *GetOneInstance();
static Singleton &GetOneInstance();
private:
Singleton();
private:
//static Singleton *m_single;
static Singleton m_single;
};
源文件
#include "Singleton.h"
//Singleton *Singleton::m_single = NULL;
Singleton::Singleton()
{
std::cout << "this is a singleton" << std::endl;
}
//Singleton *Singleton::GetOneInstance()
//{
// if (m_single == nullptr)
// {
// m_single = new Singleton();
// }
// return m_single;
//}
Singleton &Singleton::GetOneInstance()
{
static Singleton m_single;
return m_single;
}
这两种方法都一样
这种方法在单线程时应该时没啥问题的,如果在多现场下可能会创建多个实例。
#pragma once
#include <iostream>
#include <mutex>
using namespace std;
class Singleton
{
public:
static Singleton *GetOneInstance();
private:
Singleton();
private:
static Singleton *m_single;
};
#include "Singleton.h"
Singleton *Singleton::m_single = NULL;
std::mutex m_mutex;
Singleton::Singleton()
{
std::cout << "this is a singleton" << std::endl;
}
Singleton *Singleton::GetOneInstance()
{
if (m_single == nullptr)
{
static std::lock_guard<std::mutex> lock(m_mutex);
if (m_single == nullptr)
{
m_single = new Singleton();
}
}
return m_single;
}
注意:为什么实例化的时候要判断两次是否为空呢?
第一次判断为空的目的时看是否有实例创建好。
第二次判断为空是在加锁后,只要加锁后,多个现场就只能有一个现场进行访问,然后进行创建实例。
不过上述方法类是一种懒汉式单例模式,指的是在调用的时候才进行加载并创建实例。
还有一种是饿汉式单例模式,在资源初始化的时候就实例好,如果一开始实例好也就用不上上述的方法还得进行加锁进行防护。