单例
来源:互联网 发布:python db2 连接 编辑:程序博客网 时间:2024/04/27 03:37
以下观点都不是绝对的,都是相对的,谢谢
(1) 全局变量的缺点
a. 可扩展性较差
b. 维护性较差,在大型工程中可能存在多人同时开发,谁对全局变量进行了什么操作,无从得知
(2) 何为单例模式?
单例模式是24种设计模式中比较常用的一种,顾名思义就是保证某个类的实例只有一个。
(3) 单例模式实现的问题
一般来说单例模式的实现存在如下问题:
a. 如何保证外部不会生成这个类的多个实例
类的实例在一个进程中不外乎4种存储形式,全局变量, static变量,堆变量,栈变量。都是通过调用构造函数的形式来实现变量的内存空间分配和初始化,所以只要禁止外界对构造函数的调用,即可达到该目的。很明显将构造函数声明为protected或者private即可达到该目的。同理,将拷贝构造函数和赋值函数也需要声明为protected或者private.这样当显式的去实现某个类的4种形式的变量是,就会得到编译错误。
Class Singleton
{
Public:
~Singleton();
Protected:
Singleton(); //构造函数
Singleton(const Singleton&); //拷贝构造函数
Singleton& operator = (const Singleton&); //赋值函数
};
b. 如何获取单例模式的句柄
上一步我们已经初步实现了一个不能够被直接初始化的类:Singleton,
但是如何实现这个类的实例,并且获取这个唯一的句柄呢?
不要忘记C++的类方法和类变量(类中的static方法和static变量)
class Singleton
{
public:
~Singleton()
static Singleton* GetHandle()
{
if (Singleton::sHandle == NULL)
{
Singleton::sHandle = new Singleton();
}
}
protected:
Singleton(); //构造函数
Singleton(const Singleton&); //拷贝构造函数
Singleton& operator = (const Singleton&); //赋值函数
private:
static Singleton* sHandle;
};
Singleton* Singleton::sHandle = NULL;
如上,我们便实现了一个根据类方法获取一个实例句柄,目前来看基本达到了我们前面的要求。
c. 如何保证线程安全
不要诧异,我们很多情况都是在多线程中运行的。
如下语句在多线程环境中会造成多个实例:
static Singleton* GetHandle()
{
if (Singleton::sHandle == NULL)
{
Singleton::sHandle = new Singleton();
}
}
如果你确保第一次调用肯定不会有多个线程的情况,那么也不需要考虑这个问题。
但是不保证别人使用你的代码时,也考虑了这个问题
class Singleton
{
public:
~Singleton();
static Singleton* GetHandle()
{
if (!Singleton::sm_flag)
{
pthread_mutex_lock(&sm_mutex);
if (!Singleton::sm_flag)
{
Singleton::sHandle = new Singleton();
sm_flag = true;
}
pthread_mutex_unlock(&sm_mutex);
}
return Singleton::sHandle;
}
protected:
Singleton()
{
cout<<"Singleton()"<<endl;
}
Singleton(const Singleton&);
Singleton& operator = (const Singleton&);
private:
static Singleton* sHandle;
static pthread_mutex_t sm_mutex;
static bool sm_flag;
};
Singleton* Singleton::sHandle = NULL;
pthread_mutex_t Singleton::sm_mutex = PTHREAD_MUTEX_INITIALIZER;
bool Singleton::sm_flag = false;
d. 何时析构
如果按照上面的实现,我们的单例模式是随进程消亡而消亡的,是看不到析构函数的调用的,这样做是否有问题?
当然有了, 如果单例中存放的是某种资源,比如数据库连接,socket, 进程间的通信方式等就比较麻烦了。
如何析构呢?
下面这个类巧妙利用static变量的自动析构属性来析构单例的句柄
#include <iostream>
#include <pthread.h>
using namespace std;
class Singleton
{
class InstanceHandler
{
public:
InstanceHandler() : m_Handler(NULL)
{
}
~InstanceHandler()
{
if (NULL != m_Handler)
{
delete m_Handler;
}
}
Singleton* get() const
{
return m_Handler;
}
void set(Singleton* p)
{
this ->m_Handler = p;
}
protected:
InstanceHandler(const InstanceHandler&);
InstanceHandler& operator = (const InstanceHandler&);
private:
Singleton* m_Handler;
};
public:
~Singleton()
{
cout<<"~Singleton()"<<endl;
}
static Singleton* GetHandle()
{
if (!Singleton::sm_flag)
{
pthread_mutex_lock(&sm_mutex);
if (!Singleton::sm_flag)
{
sm_handler.set(new Singleton());
sm_flag = true;
}
pthread_mutex_unlock(&sm_mutex);
}
return sm_handler.get();
}
protected:
Singleton()
{
cout<<"Singleton()"<<endl;
}
Singleton(const Singleton&);
Singleton& operator = (const Singleton&);
private:
static Singleton* sHandle;
static pthread_mutex_t sm_mutex;
static bool sm_flag;
static InstanceHandler sm_handler;
};
Singleton* Singleton::sHandle = NULL;
pthread_mutex_t Singleton::sm_mutex = PTHREAD_MUTEX_INITIALIZER;
bool Singleton::sm_flag = false;
Singleton::InstanceHandler Singleton::sm_handler;
int main()
{
{
Singleton* pHandle = Singleton::GetHandl
}
}
e. 析构的优先级
当系统中存在多个实例的时候,且某些实例依赖于其他实例,也就是说实例的析构必须要有先后顺序,这个时候上面的办法就行不通了。
这个时候我可以利用标准库中的atexit函数, 如下所示:
且atexit函数具有后进先出的特点,刚好可以满足我们一定的析构顺序
#include <iostream>
#include <pthread.h>
#include <stdlib.h>
using namespace std;
class Singleton
{
public:
~Singleton()
{
cout<<"~Singleton()"<<endl;
}
static Singleton* GetHandle()
{
if (!Singleton::sm_flag)
{
pthread_mutex_lock(&sm_mutex);
if (!Singleton::sm_flag)
{
Singleton::sHandle = new Singleton();
sm_flag = true;
}
pthread_mutex_unlock(&sm_mutex);
}
}
static void Destroy()
{
if (NULL != Singleton::sHandle)
{
delete Singleton::sHandle;
}
}
protected:
Singleton()
{
cout<<"Singleton()"<<endl;
}
Singleton(const Singleton&);
Singleton& operator = (const Singleton&);
private:
static Singleton* sHandle;
static pthread_mutex_t sm_mutex;
static bool sm_flag;
};
Singleton* Singleton::sHandle = NULL;
pthread_mutex_t Singleton::sm_mutex = PTHREAD_MUTEX_INITIALIZER;
bool Singleton::sm_flag = false;
int main()
{
atexit(Singleton::Destroy);
{
Singleton* pHandle = Singleton::GetHandle();
}
}
f. 单例模式模板化
如果每个类都去这样写,岂不是很麻烦,这里提供1个模板的实现:
#include <iostream>
#include <pthread.h>
#include <stdlib.h>
using namespace std;
template <typename T>
class Singleton
{
public:
static T* GetHandle()
{
if (!Singleton::sm_flag)
{
pthread_mutex_lock(&sm_mutex);
if (!Singleton::sm_flag)
{
Singleton::sHandle = new T();
sm_flag = true;
}
pthread_mutex_unlock(&sm_mutex);
}
}
static void Destroy()
{
if (NULL != Singleton::sHandle)
{
delete Singleton::sHandle;
}
}
protected:
Singleton();
~Singleton();
Singleton(const Singleton&);
Singleton& operator = (const Singleton&);
private:
static T* sHandle;
static pthread_mutex_t sm_mutex;
static bool sm_flag;
};
template <typename T>
T* Singleton<T>::sHandle = NULL;
template <typename T>
pthread_mutex_t Singleton<T>::sm_mutex = PTHREAD_MUTEX_INITIALIZER;
template <typename T>
bool Singleton<T>::sm_flag = false;
class A
{
friend class Singleton<A>;
public:
~A()
{
cout<<"~A()"<<endl;
}
protected:
A()
{
cout<<"A()"<<endl;
}
};
int main()
{
atexit(Singleton<A>::Destroy);
{
A* pHandle = Singleton<A>::GetHandle();
}
}
g. 这里有一点需要注意一下,为了防止别人可以直接delete 单例类的句柄,需要将每个放入模版中的
类的析构函数声明为protected, 这样就可以防止直接delete调用。
以上实现均是轻量级的“山寨”版本实现,优点是自己可以根据实际需要进行构造。
如果有想体验真正强大的单例模式模板应用,请参见Andrei Alexandrescu 的Loki。
参考书籍 Andrei Alexandrescu 的<< Modern C++ Design >>
侯捷翻译<<C++程序设计新思维>>