一:
1 教科书里的单例模式
我们都很清楚一个简单的单例模式该怎样去实现:构造函数声明为private或protect防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法也返回单例类唯一的实例。
上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class
singleton
{
protected
:
singleton(){}
private
:
static
singleton* p;
public
:
static
singleton* instance();
};
singleton* singleton::p = NULL;
singleton* singleton::instance()
{
if
(p == NULL)
p =
new
singleton();
return
p;
}
|
这是一个很棒的实现,简单易懂。但这是一个完美的实现吗?不!该方法是线程不安全的,考虑两个线程同时首次调用instance方法且同时检测到p是NULL值,则两个线程会同时构造一个实例给p,这是严重的错误!同时,这也不是单例的唯一实现!
2 懒汉与饿汉
单例大约有两种实现方法:懒汉与饿汉。
-
- 懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化,所以上边的经典方法被归为懒汉实现;
- 饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
特点与选择:
-
- 由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
- 在访问量较小时,采用懒汉实现。这是以时间换空间。
3 线程安全的懒汉实现
线程不安全,怎么办呢?最直观的方法:加锁。
方法1:加锁的经典懒汉实现:
class singleton { protected: singleton() { pthread_mutex_init(&mutex); } private: static singleton* p; public: static pthread_mutex_t mutex; static singleton* initance(); }; pthread_mutex_t singleton::mutex; singleton* singleton::p = NULL; singleton* singleton::initance() { if (p == NULL) { pthread_mutex_lock(&mutex); if (p == NULL) p = new singleton(); pthread_mutex_unlock(&mutex); } return p; }
方法2:内部静态变量的懒汉实现
此方法也很容易实现,在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法,真得非常简单。
class singleton { protected: singleton() { pthread_mutex_init(&mutex); } public: static pthread_mutex_t mutex; static singleton* initance(); int a; }; pthread_mutex_t singleton::mutex; singleton* singleton::initance() { pthread_mutex_lock(&mutex); static singleton obj; pthread_mutex_unlock(&mutex); return &obj; }
4 饿汉实现
为什么我不讲“线程安全的饿汉实现”?因为饿汉实现本来就是线程安全的,不用加锁。为啥?自己想!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
singleton
{
protected
:
singleton()
{}
private
:
static
singleton* p;
public
:
static
singleton* initance();
};
singleton* singleton::p =
new
singleton;
singleton* singleton::initance()
{
return
p;
}
|
是不是特别简单呢?
以空间换时间,你说简单不简单?
二:
本文结合一些已有的C++ 单例模式 设计方法,总结出了5种实现方式,并指出其中的使用特点和注意事项;
参考引用的文章链接:
https://segmentfault.com/q/1010000004157283
一般情况下,为了实现单例我们都会想到使用 static 成员,下面#1是最基本的方式;
#1 静态指针成员:
- class Singleton{
- private:
- Singleton(){};
- virtual ~Singleton(){};
- public:
- Singleton *Instance();
- protect:
- static Singleton *_instance;
- };
- //Singleton.h
- Singleton *Singleton::instance()
- {
- if(NULL == _instance){
- _instance = new Singleton();
- }
- return _instance;
- }
- //Singleton.cpp
class Singleton{
private:
Singleton(){};
virtual ~Singleton(){};
public:
Singleton *Instance();
protect:
static Singleton *_instance;
};
//Singleton.h
Singleton *Singleton::instance()
{
if(NULL == _instance){
_instance = new Singleton();
}
return _instance;
}
//Singleton.cpp
构造时机: 运行时生成;
对象位置: 堆
资源释放: new的单例对象,没有时机去释放;
线程安全: 否; ---在单例构造过程可能重复,造成内存泄露;
#2 静态指针成员(改进型):
在#1的基础上解决存在的两个问题;
如果单例对象的构造实在运行时之前(也就是程序静态变量初始化时完成)就可以避免线程安全的问题;
- class Singleton{
- public:
- static Singleton *instance();
- private:
- Singleton();
- virtual ~Singleton();
- Singleton(const Singleton&){};
- Singleton& operator=(const Singleton&){};
- private:
- class CGarbo{
- public:
- ~CGarbo()
- {
- if(Singleton::m_pInstance){
- delete m_pInstance;
- }
- }
- };
- private:
- static Singleton *m_pInstance;
- static CGarbo Garbo;
- };
- //Singleton.h
- Singleton::CGarbo Singleton::Garbo;
- Singleton* Singleton::m_pInstance = new Singleton();
- Singleton::Singleton()
- {
- printf("contructure funcation\n");
- }
- Singleton::~Singleton()
- {
- printf("deconstructure funcation\n");
- }
- Singleton* Singleton::instance()
- {
- return m_pInstance;
- }
- //Singleton.cpp
class Singleton{
public:
static Singleton *instance();
private:
Singleton();
virtual ~Singleton();
Singleton(const Singleton&){};
Singleton& operator=(const Singleton&){};
private:
class CGarbo{
public:
~CGarbo()
{
if(Singleton::m_pInstance){
delete m_pInstance;
}
}
};
private:
static Singleton *m_pInstance;
static CGarbo Garbo;
};
//Singleton.h
Singleton::CGarbo Singleton::Garbo;
Singleton* Singleton::m_pInstance = new Singleton();
Singleton::Singleton()
{
printf("contructure funcation\n");
}
Singleton::~Singleton()
{
printf("deconstructure funcation\n");
}
Singleton* Singleton::instance()
{
return m_pInstance;
}
//Singleton.cpp
构造时机: 初始化时生成;
对象位置: 堆
资源释放: 通过一个成员的析构函数来释放单例对象,; 非常巧妙, 但是m_pInstance指针和 Garbo两个成员的析构是否有先后顺序,如果指针先被释放(指针变量变成NULL?)那么单例对象还是没有机会被释放; ---找时间确认一下,然后更新一下这个结果;
线程安全: 是; ---初始化过程,没有线程竞争;
对比以上两种方式,可以看出静态初始化的时候构造单例对象,能够比较好的解决线程安全的问题; 但是资源释放的需要通过曲线救国的方式来解决;
那么能不能把单例对象也生成在静态区呢?这样释放的问题就可以由操作系统自动完成;
#3 静态成员对象
- class Singleton{
- public:
- static Singleton *instance();
- private:
- Singleton();
- virtual ~Singleton();
- Singleton(const Singleton&){};
- Singleton& operator=(const Singleton&){};
- private:
- static Singleton m_instance;
- };
- //Singleton.h
- Singleton Singleton::m_instance;
- Singleton::Singleton()
- {
- printf("contructure funcation\n");
- }
- Singleton::~Singleton()
- {
- printf("deconstructure funcation\n");
- }
- Singleton *Singleton::instance()
- {
- return &m_instance;
- }
- //Singleton.cpp
class Singleton{
public:
static Singleton *instance();
private:
Singleton();
virtual ~Singleton();
Singleton(const Singleton&){};
Singleton& operator=(const Singleton&){};
private:
static Singleton m_instance;
};
//Singleton.h
Singleton Singleton::m_instance;
Singleton::Singleton()
{
printf("contructure funcation\n");
}
Singleton::~Singleton()
{
printf("deconstructure funcation\n");
}
Singleton *Singleton::instance()
{
return &m_instance;
}
//Singleton.cpp
构造时机: 初始化时生成;
对象位置: 静态区
资源释放: 在程序结束时 自动释放静态区的 成员变量
线程安全: 是;
注意: 静态成员的初始化需要放到类外完成; 那么是否可以把静态对象在成员函数instance()内部生成呢?
#4 静态局部对象
- class Singleton{
- public:
- static Singleton *instance();
- private:
- Singleton();
- virtual ~Singleton();
- Singleton(const Singleton&){};
- Singleton& operator=(const Singleton&){};
- };
- //Singleton.h
- Singleton::Singleton()
- {
- printf("contructure funcation\n");
- }
- Singleton::~Singleton()
- {
- printf("deconstructure funcation\n");
- }
- Singleton *Singleton::instance()
- {
- static Singleton ins;
- return &ins;
- }
- //Singleton.cpp
class Singleton{
public:
static Singleton *instance();
private:
Singleton();
virtual ~Singleton();
Singleton(const Singleton&){};
Singleton& operator=(const Singleton&){};
};
//Singleton.h
Singleton::Singleton()
{
printf("contructure funcation\n");
}
Singleton::~Singleton()
{
printf("deconstructure funcation\n");
}
Singleton *Singleton::instance()
{
static Singleton ins;
return &ins;
}
//Singleton.cpp
构造时机: 运行时生成; ---参考链接2 ; --对于C++的局部类对象,是在函数第一次调用时生成;
对象位置: 静态区
资源释放: 在程序结束时 自动释放静态区的 成员变量
线程安全: 是(C++11); ---参考链接2,, C++11标准针对局部静态对象的构造能保证线程安全(待查);
综合以上4中方式,单例对象的释放都是在程序结束时释放,
如果要求能够提供接口随时释放对象,那么就必须构造在堆上,然后提供显式的destroy接口;
#5 静态指针成员(动态释放)
- #ifndef __MUTEX_H__
- #define __MUTEX_h__
- #include <pthread.h>
- class Mutex{
- private:
- pthread_mutex_t m_mutex;
- public:
- Mutex();
- virtual ~Mutex();
- private:
- Mutex(const Mutex&){};
- Mutex& operator=(const Mutex&){};
- public:
- int lock();
- int unlock();
- int trylock();
- };
- #endif //__MUTEX_H__
- //Mutex.h
- #include "Mutex.h"
- Mutex::Mutex()
- {
- pthread_mutex_init(&m_mutex, NULL);
- }
- Mutex::~Mutex()
- {
- pthread_mutex_destroy(&m_mutex);
- }
- int Mutex::lock()
- {
- return pthread_mutex_lock(&m_mutex);
- }
- int Mutex::unlock()
- {
- return pthread_mutex_unlock(&m_mutex);
- }
- int Mutex::trylock()
- {
- return pthread_mutex_trylock(&m_mutex);
- }
- //Mutex.cpp
- //单例类
- #ifndef __SINGLETON_H__
- #defile __SINGLETON_H__
- #include "Mutex.h"
- class Singleton{
- public:
- static Singleton *instance();
- static void destroy();
- private:
- Singleton();
- virtual ~Singleton();
- Singleton(const Singleton&){};
- Singleton& operator=(const Singleton&){};
- private:
- static Singleton *m_pInstance;
- static Mutex m_Mutex;
- };
- #endif //__SINGLETON_H__
- //Singleton.h
- Singleton* Singleton::m_pInstance = NULL;
- Mutex Singleton::m_Mutex;
- Singleton::Singleton()
- {
- //do something
- }
- Singleton::~Singleton()
- {
- //do something
- }
- Singleton* Singleton::instance()
- {
- if(NULL == m_pInstance){
- m_Mutex.lock();
- if(NULL == m_pInstance){
- m_pInstance = new Singleton();
- }
- m_Mutex.unlock();
- }
- return m_pInstance;
- }
- void Singleton::destroy()
- {
- if(m_pInstance){
- m_Mutex.lock();
- if(m_pInstance){
- delete m_pInstance;
- m_pInstance = NULL;
- }
- m_Mutex.unlock();
- }
- }
- //Singleton.cpp
#ifndef __MUTEX_H__
#define __MUTEX_h__
#include <pthread.h>
class Mutex{
private:
pthread_mutex_t m_mutex;
public:
Mutex();
virtual ~Mutex();
private:
Mutex(const Mutex&){};
Mutex& operator=(const Mutex&){};
public:
int lock();
int unlock();
int trylock();
};
#endif //__MUTEX_H__
//Mutex.h
#include "Mutex.h"
Mutex::Mutex()
{
pthread_mutex_init(&m_mutex, NULL);
}
Mutex::~Mutex()
{
pthread_mutex_destroy(&m_mutex);
}
int Mutex::lock()
{
return pthread_mutex_lock(&m_mutex);
}
int Mutex::unlock()
{
return pthread_mutex_unlock(&m_mutex);
}
int Mutex::trylock()
{
return pthread_mutex_trylock(&m_mutex);
}
//Mutex.cpp
//单例类
#ifndef __SINGLETON_H__
#defile __SINGLETON_H__
#include "Mutex.h"
class Singleton{
public:
static Singleton *instance();
static void destroy();
private:
Singleton();
virtual ~Singleton();
Singleton(const Singleton&){};
Singleton& operator=(const Singleton&){};
private:
static Singleton *m_pInstance;
static Mutex m_Mutex;
};
#endif //__SINGLETON_H__
//Singleton.h
Singleton* Singleton::m_pInstance = NULL;
Mutex Singleton::m_Mutex;
Singleton::Singleton()
{
//do something
}
Singleton::~Singleton()
{
//do something
}
Singleton* Singleton::instance()
{
if(NULL == m_pInstance){
m_Mutex.lock();
if(NULL == m_pInstance){
m_pInstance = new Singleton();
}
m_Mutex.unlock();
}
return m_pInstance;
}
void Singleton::destroy()
{
if(m_pInstance){
m_Mutex.lock();
if(m_pInstance){
delete m_pInstance;
m_pInstance = NULL;
}
m_Mutex.unlock();
}
}
//Singleton.cpp
当然 也可以吧 静态成员指针放到接口函数里面作为局部静态变量 和局部静态对象;