设计模式:设计模式是一套被反复使用、多数人知晓、经过分类的、代码设计经验的总结
单例模式:一个类只能创建一个对象,保证系统中该类只有一个实例,并且提供一访问该类资源的访问点。因为有些时候,保持类的实例只有一个是非常必要的。例如:一个表示文件系统的类,一个操作系统一定是只有一个文件系统的。因此,我们就希望表示文件系统的类的实例只有一个
单例模式的实现有两种模式:
-
饿汉模式:就是不管你以后会不会用,程序启动时候都会创建一个唯一的实例对象
-
懒汉模式:懒汉模式秉承着能够晚一点构造就晚一点构造的思想,直到第一次使用单例时才会创建该类的唯一实例对象。也是延迟加载的一种体现
饿汉模式:
class Singleton
{
public:
static Singleton* GetInstance()
{
return &_instance;
}
int a = 10;
private:
//构造函数私有
Singleton()
{};
//C++98 防拷贝
//Singleton(Singleton const&);
//Singleton& operator=(Singleton const&);
//or
//C++11 防拷贝
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static Singleton _instance;
};
Singleton Singleton::_instance; //在程序入口前就会完成初始化
-
优点:实现起来比较简单
-
缺点:可能会导致进程启动缓慢,并且如果有多个单例类对象实例,各个单例类对象创建顺序不确定
-
建议用 C++ 11 防拷贝
懒汉模式 1.0:
class Singleton
{
public:
static Singleton * GetInstance()
{
if (nullptr == _pInstance)
{
_pInstance = new Singleton;
}
return _pInstance;
}
private:
//构造函数私有化
Singleton()
{};
//实现一个内嵌的回收类
class CGarbo
{
public:
~CGarbo()
{
if (_pInstance)
delete _pInstance;
}
};
//C++ 98 防拷贝
//Singleton(Singleton const&);
//Singleton& operator= (Singleton const&);
//or
//C++ 11 防拷贝
Singleton(Singleton const&) = delete;
Singleton& operator= (Singleton const&) = delete;
//定义一个静态成员变量,程序结束时调用它的析构函数,从而释放单例类对象
static CGarbo Garbo;
static Singleton* _pInstance; //单例对象指针
};
Singleton* Singleton::_pInstance = nullptr; //单例类中指针置空
Singleton::CGarbo Singleton::Garbo; //内部回收类静态对象初始化
-
因为懒汉模式中的单例类对象实例是new出来的,所以就需要我们用户自己去手动delete去释放资源,因此可以在单例类内部实现一个嵌套的回收类,这个回收类的析构函数来做对单例类对象实例的delete工作,通过在单例类内部定义一个回收类的静态成员变量,当程序结束时调用它的析构函数,从而释放析构类
-
这种实现在单线程下是没有问题的,但是在多线程的情况下,如果多个线程同时首次去调用GetInstance方法,并且检测到_pInstance是nullptr,则这多个线程将都会new一个实例给_pInstance,这样就会发生错误
懒汉模式 1.1 (加互斥锁):
class Singleton
{
public:
static Singleton * GetInstance()
{
//通过互斥锁保证多线程环境下对于临界资源的访问
_mtx.lock();
if (nullptr == _pInstance)
{
_pInstance = new Singleton;
}
_mtx.unlock();
return _pInstance;
}
private:
//构造函数私有化
Singleton()
{};
//实现一个内嵌的回收类
class CGarbo
{
public:
~CGarbo()
{
if (_pInstance)
{
delete _pInstance;
}
}
};
//C++ 98 防拷贝
//Singleton(Singleton const&);
//Singleton& operator= (Singleton const&);
//or
//C++ 11 防拷贝
Singleton(Singleton const&) = delete;
Singleton& operator= (Singleton const&) = delete;
//定义一个静态成员变量,程序结束时调用它的析构函数,从而释放单例类对象
static CGarbo Garbo;
static Singleton* _pInstance; //单例对象指针
static std::mutex _mtx; //定义一个静态的互斥锁
};
Singleton* Singleton::_pInstance = nullptr; //单例类中指针置空
Singleton::CGarbo Singleton::Garbo; //内部回收类静态对象初始化
std::mutex Singleton::_mtx; //对互斥锁初始化
-
通过加锁保证临界资源的互斥访问,可以保证线程安全
-
但是这种加锁方式会导致每次调用GetInstance方法时都会有加锁解锁的消耗,
懒汉模式 1.2 (双重检查锁):
class Singleton
{
public:
static Singleton * GetInstance()
{
if (nullptr == _pInstance)
{
//通过互斥锁保证多线程环境下对于临界资源的访问
_mtx.lock();
if (nullptr == _pInstance)
{
_pInstance = new Singleton;
}
_mtx.unlock();
}
return _pInstance;
}
private:
//构造函数私有化
Singleton()
{};
//实现一个内嵌的回收类
class CGarbo
{
public:
~CGarbo()
{
if (_pInstance)
{
delete _pInstance;
}
}
};
//C++ 98 防拷贝
//Singleton(Singleton const&);
//Singleton& operator= (Singleton const&);
//or
//C++ 11 防拷贝
Singleton(Singleton const&) = delete;
Singleton& operator= (Singleton const&) = delete;
//定义一个静态成员变量,程序结束时调用它的析构函数,从而释放单例类对象
static CGarbo Garbo;
static Singleton* _pInstance; //单例对象指针
static std::mutex _mtx; //定义一个静态的互斥锁
};
Singleton* Singleton::_pInstance = nullptr; //单例类中指针置空
Singleton::CGarbo Singleton::Garbo; //内部回收类静态对象初始化
std::mutex Singleton::_mtx; //对互斥锁初始化
-
在之前互斥锁保证线程安全的基础上,又新增了一次检查,只在第一次创建的时候进行加锁,当_pInstance不为空时不加锁,减少了不必要的加锁消耗,提高了效率
-
但是在new失败抛异常以及跳转等情况下可能会导致加锁后造成死锁的情况
懒汉模式 1.3 (lock_guard):
class Singleton
{
public:
static Singleton * GetInstance()
{
if (nullptr == _pInstance)
{
//将互斥锁交给lock_guard去管理,避免因为new抛异常等问题导致加锁后造成死锁
std::lock_guard<std::mutex> lck(_mtx);
if (nullptr == _pInstance)
{
_pInstance = new Singleton;
}
}
return _pInstance;
}
private:
//构造函数私有化
Singleton()
{};
//实现一个内嵌的回收类
class CGarbo
{
public:
~CGarbo()
{
if (_pInstance)
{
delete _pInstance;
}
}
};
//C++ 98 防拷贝
//Singleton(Singleton const&);
//Singleton& operator= (Singleton const&);
//or
//C++ 11 防拷贝
Singleton(Singleton const&) = delete;
Singleton& operator= (Singleton const&) = delete;
//定义一个静态成员变量,程序结束时调用它的析构函数,从而释放单例类对象
static CGarbo Garbo;
static Singleton* _pInstance; //单例对象指针
static std::mutex _mtx; //定义一个静态的互斥锁
};
Singleton* Singleton::_pInstance = nullptr; //单例类中指针置空
Singleton::CGarbo Singleton::Garbo; //内部回收类静态对象初始化
std::mutex Singleton::_mtx; //对互斥锁初始化
-
基于RAII 的思想将互斥锁交给lock_guard对象去管理,构造时加锁,析构时解锁。因为是临时对象,当函数栈桢结束时便会调用析构解锁,可以有效的避免死锁
懒汉模式版本 2 (静态局部变量):
class Singleton
{
public:
static Singleton * GetInstance()
{
//静态局部变量
static Singleton Instance;
return &Instance;
}
private:
//构造函数私有化
Singleton()
{};
//C++98防拷贝
//Singleton(Singleton const&);
//Singleton& operator= (Singleton const&);
//or
//C++11防拷贝
Singleton(Singleton const&) = delete;
Singleton& operator= (Singleton const&) = delete;
};
-
关于静态局部变量的线程安全: 在C++0x(即C++11)标准中,规定了编译器必须完成static变量的线程安全问题