单例模式
单例模式(singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点
通常我们可以让一个全局变量使得仅有一个对象被访问,但他不能防止你实例化多个对象,一个最好的办法是,让类对象自身负责保存他的唯一实例,这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法
Singleton类,定义一个GetInstance操作,允许客户访问他的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例
多线程时的单例
在多线程的程序中,如果有多个线程同时调用Singleton类的GetInstance()方法,可能会造成创建多个实例的结果,这里可以通过加锁来解决这个问题
lock:确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待,直到该对象被释放
临界区:临界区是保证在某一个时间只有一个线程可以访问数据的方法。使用它的过程中,需要给各个线程提供一个共享的临界区对象,无论哪个线程占有临界区对象,都可以访问受到保护的数据,这时候其它的线程需要等待,直到该线程释放临界区对象为止,临界区被释放后,另外的线程可以强占这个临界区,以便访问共享的数据。临界区对应着一个CcriticalSection对象,当线程需要访问保护数据时,调用临界区对象的Lock()成员函数;当对保护数据的操作完成之后,调用临界区对象的Unlock()成员函数释放对临界区对象的拥有权,以使另一个线程可以夺取临界区对象并访问受保护的数据。
静态指针的释放
大多时候,这样的实现都不会出现问题。有经验的读者可能会问,m_pInstance指向的空间什么时候释放呢?更严重的问题是,这个实例的析构操作什么时候执行?
如果在类的析构行为中有必须的操作,比如关闭文件,释放外部资源,那么上面所示的代码无法实现这个要求。我们需要一种方法,正常地删除该实例。
可以在程序结束时调用GetInstance并对返回的指针调用delete操作。这样做可以实现功能,但是不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用GetInstance函数。
一个妥善的方法是让这个类自己知道在合适的时候把自己删除。或者说把删除自己的操作挂在系统中的某个合适的点上,使其在恰当的时候自动被执行。
我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的CGarbo类(Garbo意为垃圾工人)
类CGarbo被定义为CSingleton的私有内嵌类,以防该类被在其它地方滥用。
在程序运行结束时,系统会调用CSingleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。
使用这种方法释放单例对象有以下特征:
-
在单例类内部定义专有的嵌套类。
-
在单例类内定义私有的专门用于释放的静态成员。
-
利用程序在结束时析构全局变量的特性,选择最终的释放时机。
-
使用单例的代码不需要任何操作,不必关心对象的释放