[设计模式] 单例模式
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
什么是单例模式
单例模式(Singleton Pattern)是一种创建型模式它的用途很广泛。我们可以直接从字面上理解它就是单个实例化对象它确保系统中某个类只有一个实例该类自行实例化并向整个系统提供这个实例的公共访问点除了该公共访问点不能通过其他途径访问该实例。
单例模式有以下特点
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
单例模式的优点是系统中只存在一个共用的实例对象无需频繁创建和销毁对象节约了系统资源提高系统的性能。并且可以严格控制客户怎么样以及何时访问单例对象。
一般单例模式有下面几种
- 懒汉模式
- 双重锁懒汉模式
- 饿汉模式
- 静态内部类模式(登记式)
- 枚举模式。
其中后面的两个是使用Java语言特性实现的所以我们在本篇中暂不讨论。
懒汉式单例(Lazy Singleton)
懒汉式单例的特点是只有当需要使用该实例时才会去创建并使用实例。
UML类图
代码
class Singleton
{
private:
static Singleton* m_pInstance;
private:
Singleton(){
std::cout<<"Constructor"<<std::endl;
}
~Singleton() {
std::cout<<"Destructor"<<std::endl;
};
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton* getInstance() {
if(m_pInstance== nullptr) {
m_pInstance= new Singleton;
}
return m_pInstance;
}
static void deleteInstance() {
if(m_pInstance!=nullptr){
delete m_pInstance;
m_pInstance = nullptr;
}
}
};
Singleton* Singleton::m_pInstance= nullptr;
我们来分析一下这段代码的几个细节。我们将构造函数私有化这样做就可以使类不能被继承和在外部创建实例。将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值。再设置了一个静态的函数用于创建实例。
但是显然我们这里如果抛出异常容易造成内存泄漏。解决方法可以使用智能指针或者添加一个Deleter。
内存安全的懒汉式单例模式
使用智能指针解决
UML类图
代码
class Singleton
{
private:
static std::shared_ptr<Singleton> m_pInstance;
private:
Singleton(){
std::cout<<"Constructor"<<std::endl;
}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
~Singleton() {
std::cout<<"Destructor"<<std::endl;
};
void run(){throw -1;}
static std::shared_ptr<Singleton> getInstance() {
if(m_pInstance == nullptr) {
m_pInstance = std::shared_ptr<Singleton>(new Singleton);
}
return m_pInstance;
}
};
std::shared_ptr<Singleton> Singleton::m_pInstance = nullptr;
int main(){
try{
std::shared_ptr<Singleton> pInstance = Singleton::getInstance();
pInstance->run();
}catch(...){
}
}
输出
Constructor
Destructor
嵌套Deleter解决
嵌套一个静态的Deleter类在此类自动析构时删除实例
UML类图
代码
class Singleton
{
private:
static Singleton* m_pInstance;
private:
Singleton(){
std::cout<<"Constructor"<<std::endl;
}
~Singleton() {
std::cout<<"Destructor"<<std::endl;
};
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
class Deleter {
public:
~Deleter() {
if(Singleton::m_pInstance != nullptr){
delete Singleton::m_pInstance;
}
}
};
static Deleter deletor;
public:
void run(){throw -1;}
static Singleton* getInstance() {
if(m_pInstance== nullptr) {
m_pInstance= new Singleton;
}
return m_pInstance;
}
};
Singleton* Singleton::m_pInstance= nullptr;
Singleton::Deleter Singleton::deletor;
int main(){
try{
Singleton* pInstance = Singleton::getInstance();
pInstance->run();
}catch(...){
}
}
输出
Constructor
Destructor
双重锁懒汉模式(线程安全)
对于单线程我们刚才的版本的没有问题。但是对于多线程有可能引发竞态条件(Race Condition)如果线程1判断instance为空同时线程2也判断为空这样就会创建两个实例。解决方法就是使用互斥锁。
代码
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
class Singleton
{
private:
static std::shared_ptr<Singleton> m_pInstance;
static std::mutex m_mutex;
private:
Singleton(){
std::cout<<"Constructor"<<std::endl;
}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
~Singleton() {
std::cout<<"Destructor"<<std::endl;
};
static std::shared_ptr<Singleton> getInstance() {
//双重检验
if(m_pInstance == nullptr){
std::lock_guard<std::mutex> _lock(m_mutex);
if(m_pInstance == nullptr) {
m_pInstance = std::shared_ptr<Singleton>(new Singleton);
}
}
return m_pInstance;
}
};
std::shared_ptr<Singleton> Singleton::m_pInstance = nullptr;
std::mutex Singleton::m_mutex;
void function(){
std::shared_ptr<Singleton> pInstance = Singleton::getInstance();
}
int main(){
std::thread t1(function);
std::thread t2(function);
t1.join();
t2.join();
}
输出
Constructor
Destructor
饿汉式单例(Hunger Singleton)
与懒汉式不同饿汉在一开始就直接创建实例。这种方式本身就是线程安全的所以无需加锁。
代码
class Singleton
{
private:
static std::shared_ptr<Singleton> m_pInstance;
private:
Singleton(){
std::cout<<"Constructor"<<std::endl;
}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
~Singleton() {
std::cout<<"Destructor"<<std::endl;
};
void run(){throw -1;}
static std::shared_ptr<Singleton> getInstance() {
return m_pInstance;
}
};
std::shared_ptr<Singleton> Singleton::m_pInstance = std::shared_ptr<Singleton>(new Singleton());
int main(){
auto pInstance = Singleton::getInstance();
}