随笔 - 119  文章 - 290  trackbacks - 0

博客搬家了哦,请移步
叫我abc

常用链接

留言簿(12)

随笔分类

我的博客

搜索

  •  

积分与排名

  • 积分 - 299447
  • 排名 - 84

最新评论

阅读排行榜

首先声明,我所要谈的singleton并非完美的singleton实现,包含各种智能指针和线程安全之类的,那些东西想必大家都已经看过不少了。
我只是借用最基础的实现方式,谈一个大多数人可能都知道但是却没有采用的实现方案——显性初始化。这是我认为的最优方案。
下面有些地方会把singleton称为单体。

1.现有的两种singleton实现方法
   1.1惰性初始化
惰性初始化的singleton的特点有这么些:直到使用的时候才初始化,构造函数私有。代码可能如下:

T& GetSingleton()
{
 
if ( NULL == t ) t = new T;
 
return *t;
}


或者

T
& GetSingleton()
{
  
static T t;
  
return t;
}

   1.2显性初始化
显性初始化的singleton的特点有这么些:构造函数public,你需要在使用该单体之前new 一下,然后在不再需要的时候delete一下。有关的代码如下:

T& GetSingleton()
{
  
return *t;
}


T::T()
{
 t 
= this;
}


T::
~T()
{
 t 
= NULL;
}



2.选择显性初始化方法的理由
   2.1明确的构造和析构
惰性初始化,他的初始化时机在第一次调用的时候,但是何时才是第一次调用,这个代码一多起来,就不是那么好把握的了。
显性初始化的时机很简单,new即存在,并且可以很直观的调整构造函数的参数。在此一个疑惑的观点是多new了几次怎么办。。。我的观点是那么显性的操作,明知道是单体,明知道很关键,你还能多new几下,你就不能判断一下啊。。。。一般说来,稍微有点基础的人都不会出这种状况的,放心的new吧。
看了大家写的关于惰性singleton实现的文章,都描述了一个单体析构别扭的问题,因为delete T::Singleton()就像是非法入侵,虽然不会造成什么实质性的危害,但是这本身就是一个语义范畴的悖论(我瞎说的,我连语义范畴都不知道是虾米),new和delete没有配对。
   2.2继承和多态
单体也会继承和多态?会哦,而且这通常是由框架本身的约束造成的,我最近的工作就碰到了。在服务器和客户端的开发项目中,通常会有base模块,即server & client base,这个模块使用了单体类A的一些接口,但是这个单体类A在服务器模块和客户端模块可是被分别实现为多少有些不同的S和C,这些实现是无法集成到base的。
那么,惰性方法是如何考虑这个的呢?第一,static T& GetSingleton方法可以重载吗?我不知道,没试过。第二,假设static方法可以重载,但是base模块使用的是A::GetSingleton哦,那即使重载了也用不上吧。
以此相对的,显性初始化的方法就简单多了:

//服务器模块
*= new S;
A::GetSingleton();
//Ok

//客户端模块
*= new C;
A::GetSingleton();
//OK

   2.3OGRE中使用的就是这一种
最初用到OGRE的时候有些不习惯也不理解,因为他是显性初始化的,那时候我还使用惰性初始化。提OGRE并不是为了说明显性初始化好而提供什么论据,而仅仅是想说,看吧,显性初始化这种方法是有着一个优秀的库的支持的。

3.其他
   3.1singleton模板?
就那点代码写了你会死啊,我绝不在这个地方使用模板,多写个#include和public Singleton<T>比写个函数和变量更让我痛恨。
   3.2线程安全
看了大家一些关于线程安全的singleton,大家都往Singleton函数加锁了。我说。。。。如果那个单体被用于线程安全的话,仅仅是Singleton函数加锁是不够的吧。况且,XP的思想是,有需求,才添加相应的功能,用不上的代码,能不加就不加。写多线程程序的情况起始还是不太多的吧。
   3.3singleton和全局变量有什么区别?(小白创可贴)
其实没啥区别的,就是一个命名协议的问题(我是完全这么认为的)。
如果全局变量的命名协议能规定为 g_classname,比如class CBoxManager; 相应的全局变量命名为 g_pCBoxManager的话,不用singleton也可以哦。
不然的话,每个单体变量的命名都没个准,这在team work中是一个让人起小疙瘩的事情。


PS:加入代码的DLG里居然没有C/C++选项,这里可是cpp blog啊^O^~

posted on 2007-09-14 22:52 LOGOS 阅读(1593) 评论(9)  编辑 收藏 引用

FeedBack:
# re: 谈谈不一样的singleton 2007-09-15 02:56 func
显性初始化的单件在多线程下还要不要加锁?惰性初始化的貌似加锁也会出问题。。。  回复  更多评论
  
# re: 谈谈不一样的singleton 2007-09-15 08:17 LOGOS
@func
1.new的时候不用加锁,一般应该在进入多线程前在主线程把单体new出来
2.所谓多线程编码,仅仅是对Singleton()函数加锁是不够的,或者也是不必要的。将单体换成全局变量想一想,在获取全局变量的时候根本没加锁这回事。
多线程要加锁的地方在对象的算法函数中,而不是获取对象本身。
  回复  更多评论
  
# re: 谈谈不一样的singleton 2007-09-15 10:06 func
我说的就是Singleton::GetInstance()函数的加锁问题,没表达清楚~  回复  更多评论
  
# re: 谈谈不一样的singleton 2007-09-15 11:58 eXile
不错, 和我的做法一样.
  回复  更多评论
  
# re: 谈谈不一样的singleton 2007-09-15 14:37 func
汗,第二个回复是在把你回复理解错的情况下发的,又看了一眼知道你说的了,汗,无视那个回复吧……

= =  回复  更多评论
  
# re: 谈谈不一样的singleton 2007-10-06 18:48 study
"显性初始化的时机很简单,new即存在"
单体是不能new的,使用者多调几次new怎么办
你这个就是一个普通的类,不是单体,当然设计之初不需要考虑多线程环境

另外c++下不能使用类似java下的提前new 出static来 编译器的问题

惰性初始化是唯一选择,也是gof设计模式使用的方式,如果认为效率有问题,可以使用双重检测  回复  更多评论
  
# re: 谈谈不一样的singleton 2007-10-17 16:30 func
楼上应该看一下OGRE的实现,其实这种单件在构造函数中assert的不存在(保证了单件的唯一性),在获得实例的函数中assert实例的存在(保证了单件的存在性即已被初始化)

不过感觉缺点就是报错是在运行时报错,而不是编译其间。  回复  更多评论
  
# re: 谈谈不一样的singleton 2007-10-17 16:33 func
“其实这种单件在构造函数中assert单件实例不存在”

写漏了几个字  回复  更多评论
  
# re: 谈谈不一样的singleton 2007-12-24 23:02 逍遥剑客
最好所有singleton的初始化跟销毁都放在一起,因为有些singleton之间是有依赖关系的,必须按特定顺序来  回复  更多评论
  

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