SmartPtr
本博客已搬至:http://www.cnblogs.com/baiyanhuang/
posts - 29,comments - 176,trackbacks - 0
By SmartPtr(http://www.cppblog.com/SmartPtr/)

     Singleton应该可以算是GOF23个模式中最简单的一个模式了,它有两个要求:一是保证一个类仅有一个实例;二是提供一个访问它的全局访问点。这在实现中分别对应为:一是构造函数非public;二是提供一个静态函数作为全局访问点。
  在
C#中,我们可以这么写:
public class ExampleSingleton
{
    
// code to support Singleton
    protected ExampleSingleton(){}
    
protected static ExampleSingleton instance = new ExampleSingleton();

    
public static ExampleSingleton Instance
    {
        
get{return instance;}
    }

    
// This class's real functionalities
    public void Write(){Console.WriteLine("Hello, World!");}
}

// use this singleton class
ExampleSingleton.Instance.Write();
    类似的,C++中实现如下:
class ExampleSingleton
{
    
// code to support Singleton
protected:
        ExampleSingleton(){}
public:
    
static ExampleSingleton& Instance()
    {
        
static ExampleSingleton instance;
        
return instance;
    }

    
// This class's real functionalities
    void Write(){printf("Hello, World!");}
};

// use this singleton class
ExampleSingleton::Instance().Write();

    这样写的确符合了singleton的两个要求,但是如果我们的系统中有许多个Singleton类,而对于每一个类,我们都要写那些固定的,重复的代码去支持其singleton的属性。这不是一个好现象,我们希望可以把这些固定的代码提取出来,使我们的Singleton类只需要专注于实现其真正的功能,相信很多人都听说过这个做法:Singleton模板基类。
对于C#:
// Singleton base class, each class need to be a singleton should 
// derived from this class
public class Singleton<T> where T: new()
{
    
    
// Instead of compile time check, we provide a run time check
    
// to make sure there is only one instance.
    protected Singleton(){Debug.Assert(null == instance);}
    
protected static T instance = new T();
    
public static T Instance
    {
        
get{return instance;}
    }
}

// Concrete singleton class, derived from Singleton<T>
public class ExampleSingleton: Singleton<ExampleSingleton>
{
    
// since there is no "freind class" in C#, we have to make
    
// this contructor public to support the new constraint.
    public ExampleSingleton(){}

    
// This class's real functionalities
    public void Write(){Console.WriteLine("Hello, World!");}
}

// use this singleton class
ExampleSingleton.Instance.Write();

这里,我们把为了支持Singleton功能的代码提到一个Singleton<T>的类模板当中,任何需要成为Singlton的类,只需从其派生便自然获得Singleton功能。这里的一个问题是:为了支持模板类中的new()constraint,我们不得不把作为具体singleton类的派生类的构造函数作为public,这就导致我们无法在编译期阻止用户自行new出第二个,第三个实例来,但我们可以在运行期来进行检查进而保证其实例的单一性,这就是这singleton基类构造函数的作用:
protected Singleton(){Debug.Assert(null == instance);}

    当然,有另外一种实现方法,那就是singleton基类不提供new() constraint, 这样我们就可以让ExampleSingleton的构造函数为非public,在要创建T实例的时候,由于不能使用new, 我们用reflection反射出类型T的非public构造函数并调用之。这样,我们的确能保证编译期实例的唯一性,但是由于用了反射,感觉代码不是那么的简单优雅,并且对其性能持保留态度,所以不太倾向与这种方法。
    但毕竟是越早检查出错误越好,所以大家如果有好的解决方案,不妨提出来一起讨论讨论。

 

而C++中由于提供了友元这个特性,实现起来要好一些:
// Singleton base class, each class need to be a singleton should 
// derived from this class
template <class T> class  Singleton
{
protected:
    Singleton(){}
public:
    
static T& Instance()
    {
        
static T instance;
        
return instance;
    }
};

// Concrete singleton class, derived from Singleton<T>
class ExampleSingleton: public Singleton<ExampleSingleton>
{
    
// so that Singleton<ExampleSingleton> can access the 
    
// protected constructor
    friend class Singleton<ExampleSingleton>;

protected:
        ExampleSingleton(){}
public:
    
// This class's real functionalities
    void Write(){printf("Hello, World!");}
};

// use this singleton class
ExampleSingleton::Instance().Write();

在C++友元的帮助下,我们成功实现了在编译期保证实例的唯一性。(当然,前提是你不要"乱交朋友")。

    有人可能会问,实现singleton的代码并不多,我们没必要搞这么一个机制来做代码复用吧? 的确,我们复用的代码并不是很多,但是,我想代码复用的目的不仅仅是减少代码量,其最重要的目的还是在于保持行为的一致性,以便于使用与维护。(用函数替换代码段便是一个很好的例子)。
对于这里的singleton类来讲,如果不做这个设计,我们在每个具体的singleton类内部实现其singleton机制,那么可能出现的问题是
1. 很难保证其接口的一致性
张三写了一个singleton类,全局访问函数是Instance, 李四也写了一个Singleton类,全局访问函数可能就是GetInstance了。。。。。我们并没有一个健壮的机制来保证接口的一致性,从而导致了使用的混乱性。

2. 不易维护
Singleton创建实例有两种:一种为lazy initialization, 一种就是early initialization, 假如开始的实现是所有的singleton都用lazy initialization, 突然某种需求要求你用early initialization,你的噩梦就开始了,你不得不重写每一个singleton类。

而用了singleton模板基类这种机制,以上问题就不会存在,我们得到的不仅仅是节约几行代码:)

posted on 2007-07-17 23:54 SmartPtr 阅读(1810) 评论(7)  编辑 收藏 引用

FeedBack:
# re: Singleton模式在C#与C++中的实现
2007-07-18 12:16 | ChenA
呵呵,这个模式叫CRTP。
local static不是线程安全的,哎,而且在c++ 08标准出来之前,singleton不可能是线程安全的。
singleton只能在单线程里用用,基本就是鸡肋。
  回复  更多评论
  
# re: Singleton模式在C#与C++中的实现
2007-07-18 12:50 | SmartPtr
CRTP = the Curiously Recurring Template Pattern, 这里只是其应用之一, 它还可以用于模拟虚函数等.
关于Singleton的线程安全,虽然我们可以写一些代码来做到(或看起来做到)线程安全,但是由于现在C++内存模型的先天不足, 事实是无法做到。
  回复  更多评论
  
# re: Singleton模式在C#与C++中的实现
2007-07-18 14:22 | Afreet
@ChenA
小心的问一下,双检查加锁模式呢……  回复  更多评论
  
# re: Singleton模式在C#与C++中的实现
2007-07-18 16:28 | eXile
双重锁同样是不安全的,所以尽量不要采用lazy initialization,
对于early initialization, 一种安全的办法可以参见:
http://www.cppblog.com/eXile/archive/2006/09/27/13034.html  回复  更多评论
  
# re: Singleton模式在C#与C++中的实现
2007-07-18 17:07 | anders06
@ eXile
你是学Java的? 双重锁在C#里是安全的.
在Java里有人已经写了Singleton is evil  回复  更多评论
  
# re: Singleton模式在C#与C++中的实现
2007-07-18 17:26 | anders06
>>不得不把作为具体singleton类的派生类的构造函数作为public,这就导致我们无法在编译期阻止用户自行new出第二个

你都说了你的弊端了,让我怎么说你好呢:),还是老老实实写吧,到目前为止我没有找到个完美的方案.
他们说NGeneric中已经有该功能的实现,有空找来看看


  回复  更多评论
  
# re: Singleton模式在C#与C++中的实现
2007-07-18 18:34 | Afreet
eXile
回头看看,确实,DCLP由于编译器,其汇编代码是不确定一定能够按序执行的,所以同样隐藏了线程安全问题;当然在高并发情况下的瓶颈也是一个重要因素。

所以俺觉得类似于boost的early initialization可能更可取些。

至于Java中的Singleton,问题在于采用lazy initialization策略时,如果没有合理的同步,各个线程得到的实例可能不是同一个——而且这是由于JVM导致的,这就没啥好法子了。C++可以用汇编写线程库控制代码执行顺序,JVM呢?  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理