C++之单例模式
单例模式也称为单件模式、单子模式,可能是使用最广泛的设计模式。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。有很多地方需要这样的功能模块,如系统的日志输出,GUI应用必须是单鼠标,MODEM的联接需要一条且只需要一条电话线,操作系统只能有一个窗口管理器,一台PC连一个键盘。
方法一:饿汉模式,即指单例实例在程序运行时被立即执行初始化
class Singleton {
public:
static Singleton * Instance() {
return &m_pInstance;
}
private:
Singleton (); // ctor is hidden
Singleton (Singleton const&); // copy ctor is hidden
static Singleton m_pInstance;
};
// in Singleton.cpp we have to add
Singleton Singleton ::m_pInstance;
此种方法不足:如果有两个单例模式的类 ASingleton 和 BSingleton, 某天你想在 BSingleton 的构造函数中使用 ASingleton 实例, 这就出问题了. 因为 BSingleton m_pInstance 静态对象可能先 ASingleton 一步调用初始化构造函数, 结果 ASingleton::Instance() 返回的就是一个未初始化的内存区域, 程序还没跑就直接崩掉.
方法二:懒汉模式,即单例实例只在第一次被使用时进行初始化
class Singleton {
public:
static Singleton* Instance() {
if (!m_pInstance)
m_pInstance = new Singleton;
return m_pInstance;
}
private:
Singleton(); // ctor is hidden
Singleton(Singleton const&); // copy ctor is hidden
static Singleton* m_pInstance;
};
// in .cpp we have to add
Singleton* Singleton::m_pInstance = NULL;
Instance() 只在第一次被调用时为 m_pInstance 分配内存并初始化. 嗯, 看上去所有的问题都解决了, 初始化顺序有保证, 多态也没问题.
不过细心的你可能已经发现了一个问题, 程序退出时, 析构函数没被执行. 这在某些设计不可靠的系统上会导致资源泄漏, 比如文件句柄, socket 连接, 内存等等. 幸好 Linux / Windows 2000/XP 等常用系统都能在程序退出时自动释放占用的系统资源. 不过这仍然可能是个隐患,有些系统是不会自动释放的.
对于这个问题, 比较土的解决方法是, 给每个 Singleton 类添加一个 destructor() 方法,采用RAII方法:
class CSingleton:
{
public:
static CSingleton * GetInstance()
private:
CSingleton(){};
static CSingleton * m_pInstance;
class CGarbo // 它的唯一工作就是在析构函数中删除CSingleton的实例
{
public:
~CGarbo()
{
if (CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
CSingleton::m_pInstance=-=NULL;
}
};
static CGarbo Garbo; // 定义一个静态成员,在程序结束时,系统会调用它的析构函数
};
然后在程序退出时确保调用了每个 Singleton 类的 destructor() 方法, 这么做虽然可靠, 但却很是繁琐。
考虑到线程安全、异常安全,方法二可以做以下扩展:
class Lock {
private:
CCriticalSection m_cs;
public:
Lock(CCriticalSection cs) : m_cs(cs) {
m_cs.Lock();
}
~Lock() {
m_cs.Unlock();
}
}; //RAII模式
class Singleton {
private:
Singleton();
Singleton(const Singleton &);
Singleton& operator = (const Singleton &);
class CGarbo {
public:
~CGarbo() {
if (CSingleton::m_pInstance){
delete CSingleton::m_pInstance;
CSingleton::m_pInstance=-=NULL;
}
};
static CGarbo Garbo; // 定义一个静态成员,在程序结束时,系统会调用它的析构函数
public:
static Singleton *Instantialize();
static Singleton *pInstance;
static CCriticalSection cs;
};
Singleton* Singleton::pInstance = 0;
Singleton* Singleton::Instantialize()
{
if(pInstance == NULL)
{ //double check
Lock lock(cs);
//用lock实现线程安全,用资源管理类,实现异常安全
//使用资源管理类,在抛出异常的时候,资源管理类对象会被析构,析构总 是发生的无论是因为异常抛出还是语句块结束。
if(pInstance == NULL)
pInstance = new Singleton();
}
return pInstance;
}
方法三、最优实现写法
class Singleton {
public:
static Singleton& Instance() {
static Singleton theSingleton;
return theSingleton;
}
private:
Singleton(); // ctor hidden
Singleton(Singleton const&); // copy ctor hidden
Singleton& operator=(Singleton const&); // assign op. hidden
~Singleton(); // dtor hidden
};
在 Instance() 函数内定义局部静态变量的好处是, theSingleton的构造函数只会在第一次调用 Instance() 时被初始化, 达到了和 “堆栈版” 相同的动态初始化效果, 保证了成员变量和 Singleton 本身的初始化顺序.
它还有一个潜在的安全措施, Instance() 返回的是对局部静态变量的引用, 如果返回的是指针, Instance() 的调用者很可能会误认为他要检查指针的有效性, 并负责销毁. 构造函数和拷贝构造函数也私有化了, 这样类的使用者不能自行实例化.
另外, 多个不同的 Singleton 实例的析构顺序与构造顺序相反.
同样可以用Lock保证线程安全。
此处的单例模式特点如下:
1. 它有一个指唯一实例的静态指针m_pInstance,并且是私有的。
2. 它有一个公有的函数,可以获取这个唯一的实例,并在需要的时候创建该实例。
3. 它的构造函数是私有的,这样就不能从别处创建该类的实例。
参考链接:
http://stackoverflow.com/questions/86582/singleton-how-should-it-be-used
http://blog.youkuaiyun.com/taiyang1987912/article/details/43202271
http://www.cnblogs.com/wxxweb/archive/2011/04/15/2017088.html
http://www.programlife.net/cpp-singleton-memory-retrieve.html
http://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289