Singleton实作技术
Singleton, 是一种描述非常简单, 应用和实现却很复杂的奇异模式, 有大量文献对Singleton做过讨论, “设计与模式”一书也用了很大的笔墨描述了Singleton的滥用所引发的问题, 并介绍了一些对Singleton的看法,以及去掉已存在的不合理Singleton的解决方法。 当然, 本书只是讨论Singleton的实现问题, 并不涉及模式的应用。
Singleton的实现方法, 唯一性, 生命期, 多线程是本书关注的重点。
首先: Singleton的最通用的实现方法, 就是用static函数instance来访问, 并有一个静态成员变量_singleton. 然后将构造,赋值都写成私有的。
摧毁Singleton的时候, 如果Singleton是用new分配的, 那么如果不显示的调用delete, 可能会内存泄露, 而如果用scott meyers的办法, 用静态成员变量,Singleton会自动在程序结束时候被析构。这里给了一个很有趣的东西, 就是在处理全局变量和静态变量的时候, 会通过调用atexit来注册destroy函数, 这个概念我是第一次见到:). 而后面的很多生命期管理,用到的都是这个咚咚了。
介绍了Dead Reference问题, 这是个复杂的问题,出现的很微妙,一般人可能根本不会去考虑(至少我是没有考虑过). Keyboard, Log, Display3个Singleton. 创立的顺序未知,而Keyboard和Display都会依赖Log. 问题出现了: keyboard构造成功,Display构造失败, Display产生一个Log错误,程序结束。按照Singleton的出现顺序。 首先, Keyboard构造, 然后Display, 然后Display析构,Log构造。 程序关闭,先析构Log, 最后析构Keyboard. (因为静态变量的析构是后生成先析构的). 但是如果Keyboard析构失败了, 需要调用Log来生成记录,那么Log此时已经被析构掉了,于是陷入一个不可知的陷阱。 这就是Dead Reference问题, 为了解决这个问题, 有两个方案:Phoenix Singleton和带寿命的Singleton.
Phoenix是通过一个_destroy的变量来检测Singleton是否被析构了, 如果被析构了, 又被别得对象调用,那么会再次生成此Singleton. 而带寿命的Singletons是通过atexit这个函数, 来注册不同的Singleton的析构函数。 这些析构函数会根据不同的Singleton的生命排序,来达到最终效果。
最后描述了多线程, Singleton属于全局对象,而全局对象属于共享资源,在多线程的世界一定会出问题。 介绍了一些办法解释。 (没有做过多线程的东西, 虽然看起来很简单,不过没啥感想)
不过,这章最令我感兴趣的地方是: 如果将Singleton的这些需求和策略分开,也就是第一章说的policy的分离办法, 这里, 将这些策略分为Creation, LifeTime, threadingModel3个policy, 最后可以用不同的策略来实现Singleton. 非常的棒, 是一个分离策略的好例子。如果能理解上述所有,那么使用Singleton自然很简单了, 不过如果没有工作在相当复杂的环境, 我想,可能也用不到这么复杂的技巧了,呵呵。贴以下SingletonHolder的实现:
template
<
typename T,
template <class> class CreationPolicy = CreateUsingNew,
template <class> class LifetimePolicy = DefaultLifetime,
template <class, class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
class MutexPolicy = LOKI_DEFAULT_MUTEX
>
class SingletonHolder
{
public:
/// Type of the singleton object
typedef T ObjectType;
/// Returns a reference to singleton object
static T& Instance();
private:
// Helpers
static void MakeInstance();
static void LOKI_C_CALLING_CONVENTION_QUALIFIER DestroySingleton();
// Protection
SingletonHolder();
// Data
typedef typename ThreadingModel<T*,MutexPolicy>::VolatileType PtrInstanceType;
static PtrInstanceType pInstance_;
static bool destroyed_;
};
非常简单, 只要实现了各种策略, 就会得到一个理想的Singleton. 尤其在复杂环境中,尤为好用。