CG@CPPBLOG

/*=========================================*/
随笔 - 76, 文章 - 39, 评论 - 137, 引用 - 0
数据加载中……

再论Singleton

记得以前大家讨论过Singleton三种写法的优劣,今天我又发现了一个新问题,在这里和大家分享一下。下面是三种Singleton的写法:

 

 1 // 1st
 2 class Singleton{
 3     static Singleton inst;
 4 public:
 5     Singleton& GetInst(){
 6         return inst;
 7     }
 8 };
 9 Singleton Singleton::inst;
10 // 2nd
11 class Singleton{
12     static Singleton* _inst;
13 public:
14     Singleton& GetInst(){
15         if(_inst == NULL)
16             _inst = new Singleton;
17         return *_inst;
18     }
19 };
20 // 3rd
21 class Singleton{
22 public:
23     Singleton& GetInst(){
24         static Singleton inst;
25         return inst;
26     }
27 }

我们已经知道方法1没有多线程问题,但编译时分配内存。方法2有多线程问题,在运行时分配内存。方法3也有多线程问题,而且在编译时分配内存,但在运行时调用构造函数。(多线程问题的解决方法在此不再赘述。)

那么,我们可能就认为方法
1虽然在编译时分配内存,但我们不在乎这点内存,反正写出来是要用的,这点内存少不了,既避免了多线程,又避免了分配失败。就用它了!

不幸的是,方法1也有它自身的问题。今天我在构造一个对象工厂时,期望通过全局变量,向工厂注册派生类的生成方法时,方法1暴露了它的问题。比如我们在Singleton中有一个方法RegisterMethod(CallBack*),那么我可能这样实现。

 

1 //以下代码是全局的,文件级作用域
2 
3 namespace{
4     CBase* CreateDeriveObj()
5     {
6         return new(CDerive);
7     }
8     BOOL tmp = Singleton::GetInst().RegisterMethod(CreateDeriveObj);
9 }//end of namespace
 

 

结果发现在调用RegisterMethod()时,Singleton::inst 还没有初始化(构造函数没有被调用)。究其原因是编译器虽然在编译时对其分配内存,但是构造函数是在运行时,在Main()函数前调用。而对于全局变量,编译器是不保证初始化顺序的!而这个例子就是tmp在构造时, Singleton::inst 还没有构造。

而这个问题的解决方法只有使用方法2或者方法3,考虑到我们不能在Main函数前使用new操作符,我用了方法3。又因为我有全局变量保证,就可以不考虑多线程问题了。

要么是这个问题,要么是那个问题,你总要解决一个问题。

 

posted on 2007-12-20 21:55 cuigang 阅读(387) 评论(0)  编辑 收藏 引用 所属分类: C/C++


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