探究 C++ Singleton(单例模式)(二)

2014-11-24 12:22:38 · 作者: · 浏览: 1

static Singleton* getInstance()
{
if(! m_data) m_data = new Singleton();
return m_data;
}

private:
static Singleton* m_data; //static data member 在类中声明,在类外定义
Singleton(){}
~Singleton(){}
};

Singleton* Singleton::m_data = nullptr;  getInstance() 只在第一次被调用时为 m_data 分配内存并初始化. 嗯, 看上去所有的问题都解决了, 初始化顺序有保证, 多态也没问题. 但是只是看似没有问题而已,其实其中存在着两个问题:
①线程不安全:我们注意到在 static Singleton* getInstance() 方法中,是通过 if 语句判断 静态实例变量 是否被初始化来觉得是否进行初始化,那么在多线程中就有可能出现多次初始化的问题。比方说,有两个多线程同时进入到这个方法中,同时执行 if 语句的判断,那么就会出现两次两次初始化静态实例变量的情况。
②析构函数没有被执行: 程序退出时, 析构函数没被执行. 这在某些设计不可靠的 系统上会导致资源泄漏, 比如文件句柄, socket 连接, 内存等等. 幸好 Linux / Windows 2000/XP 等常用系统都能在程序退出时自动释放占用的系统资源. 不过这仍然可能是个隐患。   对于这个问题, 比较土的解决方法是, 给每个 Singleton 类添加一个 destructor() 方法:
virtual bool destructor()
{
// ... release resource
if (nullptr != m_data)
{
delete m_data;
m_data = nullptr;
}  
}    然后在程序退出时确保调用了每个 Singleton 类的 destructor() 方法, 这么做虽然可靠, 但却很是繁琐.
   四、懒汉模式改进版:使用局部静态变量
class Singleton {
public:
static Singleton& getInstance() {
static Singleton theSingleton;
return theSingleton;
}
/* more (non-static) functions here */

private:
Singleton(); // ctor hidden
Singleton(Singleton const&); // copy ctor hidden
Singleton& operator=(Singleton const&); // assign op. hidden
~Singleton(); // dtor hidden
};
但是这种方式也存在着很多的问题: ①任意两个单例类的构造函数不能相互引用对方的实例,否则会导致程序崩溃。如: ASingleton& ASingleton::getInstance() {
const BSingleton& b = BSingleton::getInstance();
static ASingleton theSingleton;
return theSingleton;
}

BSingleton& BSingleton::getInstance() {
const ASingleton & b = ASingleton::getInstance();
static BSingleton theSingleton;
return theSingleton;
}
②多个 Singleton 实例相互引用的情况下, 需要谨慎处理析构函数. 如: 初始化顺序为 ASingleton BSingleton CSingleton 的三个 Singleton 类, 其中 ASingleton BSingleton 的析构函数调用了 CSingleton 实例的成员函数, 程序退出时, CSingleton 的析构函数 将首先被调用, 导致实例无效, 那么后续 ASingleton BSingleton 的析构都将失败, 导致程序异常退出.
③在局部作用域下的静态变量在编译时,编译器会创建一个附加变量标识静态变量是否被初始化,会被编译器变成像下面这样(伪代码): static Singleton &Instance()
{
static bool constructed = false;
static uninitialized Singleton instance_;
if (!constructed) {
constructed = true;
new(&s) Singleton; //construct it
}
return instance_;
}
那么,在多线程的应用场合下必须小心使用. 如果唯一实例尚未创建时, 有两个线程同时调用创建方法, 且它们均没有检测到唯一实例的存在, 便会同时各自创建一个实例, 这样就有两个实例被构造出来, 从而违反了单例模式中实例唯一的原则. 解决这个问题的办法是为指示类是否已经实例化的变量提供一个 互斥锁 (虽然这样会降低效率).
加锁如下: static Singleton &getInstance()
{
Lock();
//锁自己实现 static
Singleton instance_;
UnLock();

return instance_;
} 但这样每次调用instance()都要加锁解锁,代价略大。
五、终极方案 在前面的讨论中,单例类中的静态对象无论是作为静态局部对象还是作为类静态全局变量都有问题,那么有什么更好的解决方案呢? boost 的实现方式是:单例对象作为静态局部变量,然后增加一个辅助类,并声明一个该辅助类的类静态成员变量,在该辅助类的构造函数中,初始化单例对象。 实现如下: class Singleton
{
public:
static Singleton* getInstance()
{
static Singleton instance;
return &instance;
}

protected:
struct Object_Creator
{
Object_Creator()
{
Singleton::getInstance();
}
};
static Object_Creator _object_creator;

Singleton() {}
~Singleton() {}
};
Singleton::Object_Creator Singleton::_object_creator;
在前面的方案中:饿汉模式中,使用到了类静态成员变量,但是遇到了初始化顺序的问题; 懒汉模式中,使用到了静态局部变量,但是存在着线程安全等问题。 那么在这个终极方案中可以说综合了以上两种方案。即采用到了类静态成员变量,也采用到了静态局部变量。
注意到其中的辅助结构体 Object_Creator (可以称之为 proxy-class)所声明的类静态成员变量,初始化该静态成员变量时,其中的构造函数 调用了单例类的 getInstance 方法。这样就会调用到 Singleton::getInstance() 方法初始化单例对象,那么自然 Singleton 的构造函数也就执行了。
我们可以在Sin