SpartacusIn21

专注:c++,python,d3d,设计模式,人工智能,并行计算

设计模式之单例模式

 意图

  保证一类仅有一个实例,并提供一个访问它的全局访问点。

适用性

  在下面的情况下可以使用Singleton模式:

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  • 当这个唯一的实例应该是通过子类可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

UML图

  • Instance

  ——定义一个Instance操作,允许客户访问它的唯一实例。

  ——可能负责创建它自己的唯一实例。

实现

 

保证一个唯一的实例

  为保证类示例的唯一性,需要提供一个统一的接口,一般是静态函数,来保证只有一个实例被创建。需要注意的是,客户只能通过Instance来访问这个单件。

//.h
class Singleton{
public:
	static Singleton * Instance();
protected:
	Singleton();
private:
	static Singleton * _isntance;
};
//.cpp
Singleton *Singleton::_isntance = 0;
Singleton *Singleton::Instance(){
	if(_isntance == 0){
		_isntance = new Singleton;
	}
	return _isntance;
}

  注意上面示例中,构造函数是protected,这样就不能在类外实例化生成对象了,保证了单例类的唯一性。

 

创建Singleton类的子类

  为保证子类的唯一实例,GOF一书中提到了单件注册表(registry of singleton)。下面用一个map对象来模拟。

//.h
class Singleton{
public:
    static void Register(const char* name, Singleton*);//注册子类单例
    static Singleton * Instance(char *name);
private:
    static Singleton * _instance;
    static std::map<string, Singleton*> _registry;
};

//.cpp
Singleton *Singleton::_instance = 0;
std::map<string, Singleton*> Singleton::_registry;
 
Singleton *Singleton::Instance(char *name){
    //if(_instance == 0){
        _instance = _registry[name];
    //}
    return _instance;
}
//注册子类单例
void Singleton::Register(const char* name, Singleton* single){
    if(_registry.count(name) > 0){
        return;
    }
    else{
        _registry.insert(std::make_pair(name, single));
    }
}

   子类MySingleton继承实现如下,只需要在构造函数中传入当前来的名称,然后调用父类的Register函数注册到父类的_registry map对象中:

//.h
class MySingleton : public Singleton{
public:
    MySingleton(char *name);
};
//.cpp
MySingleton::MySingleton(char *name){
    Singleton::Register(name, this);
}

  子类必须要创建,否则不会被注册,创建的时候一定要给当前的单例类赋值一个名称,以后每次取该单例类的时候都是通过传入名称到Instance函数来获取单例类实例指针:

#define MYSINGLETON "MySingleton"

static MySingleton theSingleton(MYSINGLETON);

MySingleton *single = Singleton::Instance(MYSINGLETON);//获取单例类实例

 

处理多线程

   虽然上面保证了单例类的唯一性,但是在多线程环境中,如果两个线程同时访问单例类Singleton的Instance函数,而且这个时候静态变量_instance为NULL,那么在线程未同步的情况下,有可能创建单例类Singleton的两个实例,为避免这种情况的发生,需要对创建实例的过程进行线程同步:

//.h
class Singleton{
public:
    static Singleton volatile * Instance();
protected:
    Singleton();
private:
    static Singleton * _instance;
};
//.cpp
Singleton *Singleton::Instance(){
	if(_instance == 0){
		//对_isntance加锁进行同步
		...
		if(_instance = 0){//获取到_instance访问权再次判断指针值,避免期间被其他线程赋值
			_instance = new Singleton;
		}
	}
    return _instance;
}

  在Instance函数中,当判断_instance为NULL后,对_instance变量或者该区域加锁,待线程成功获取锁后,需二次判断_instace的值(避免在等待锁的过程中_instance刚好被初始化),如果还是为NULL,这个时候再去新建单例类的唯一实例。

  这样一来,不管在单线程还是多线程任务环境中,单例类的唯一性得到了确切保证。

 

参考资料:

a) GOF

posted on 2017-08-07 23:36  SpartacusIn21  阅读(334)  评论(0编辑  收藏  举报

导航