Singleton单件和仿单件的各种做法探讨
来源:互联网 发布:中国人口老龄化数据 编辑:程序博客网 时间:2024/04/28 23:12
#include <iostream>
#include <string>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
class Singleton {
public:
static Singleton& Instance() {
static Singleton instance;
return instance;
}
void SetName( string name ) { name_ = name; }
void ShowName() { cout << name_ << endl; };
private:
string name_;
Singleton() {}
~Singleton() {}
Singleton( const Singleton& rhs );
Singleton& operator=( const Singleton& rhs );
};
////////////////////////////////////////////////////////////////////////////////
class MonostateFromMartin {
public:
void SetName( string name ) { name_ = name; }
void ShowName() { cout << name_ << endl; };
private:
static string name_;
};
string MonostateFromMartin::name_;
////////////////////////////////////////////////////////////////////////////////
class MonostateFromAlexandrescu {
public:
static void SetName( string name ) { name_ = name; }
static void ShowName() { cout << name_ << endl; };
private:
static string name_;
};
string MonostateFromAlexandrescu::name_;
////////////////////////////////////////////////////////////////////////////////
class MonostateMixSingleton {
public:
static void SetName( string name ) { Instance().name_ = name; }
static void ShowName() { cout << Instance().name_ << endl; };
private:
static MonostateMixSingleton& Instance() {
static MonostateMixSingleton instance;
return instance;
}
string name_;
};
////////////////////////////////////////////////////////////////////////////////
class MonostateComplex {
public:
static void SetName( string name ) { impl.SetName( name ); }
static void ShowName() { impl.ShowName(); };
private:
class MonostateComplex_ {
public:
void SetName( string name ) { name_ = name; }
void ShowName() { cout << name_ << endl; };
private:
string name_;
};
static MonostateComplex_ impl;
};
MonostateComplex::MonostateComplex_ MonostateComplex::impl;
////////////////////////////////////////////////////////////////////////////////
namespace {
string name_;
}
namespace NamespaceSinglton {
void SetName( string name ) { name_ = name; }
void ShowName() { cout << name_ << endl; };
}
////////////////////////////////////////////////////////////////////////////////
template < class T >
class SingletonHolder {
public:
static T& Instance() {
static T instance;
return instance;
}
};
////////////////////////////////////////////////////////////////////////////////
void main() {
{
Singleton::Instance().SetName( "Singleton" );
Singleton::Instance().ShowName();
}
{
MonostateFromMartin m1;
m1.SetName( "MonostateFromMartin" );
MonostateFromMartin m2;
m2.ShowName();
}
{
MonostateFromAlexandrescu::SetName( "MonostateFromAlexandrescu" );
MonostateFromAlexandrescu::ShowName();
}
{
MonostateMixSingleton::SetName( "MonostateMixSingleton" );
MonostateMixSingleton::ShowName();
}
{
MonostateComplex::SetName( "MonostateComplex" );
MonostateComplex::ShowName();
}
{
NamespaceSinglton::SetName( "NamespaceSinglton" );
NamespaceSinglton::ShowName();
}
{
SingletonHolder< const char* >::Instance() = "abc";
}
}
综合比较
除Singleton以外的做法习惯上我把他们叫做仿单件。
Singleton的做法是标准的单件做法,但是每次都要以Instance()调用,颇不方便。
MonostateFromMartin的做法可以让多个对象拥有同一份数据。
后面的做法除了MonostateMixSingleton外都是类namespace(class+static)的做法,不如最后的 NamespaceSinglton来得简捷。但有一个问题,如果你要在main之前利用启动码(《C++设计新思维》p204)调用其静态成员函数(或者名 字空间的函数)就会产生初始化顺序未定义的问题,就是函数被调用时相应的静态变量(或者名字空间变量)可能尚未产生。比如:
bool startup() {
NamespaceSinglton::SetName( "NamespaceSinglton" );
return true;
}
bool startup__ = startup();
上述代码若在main()以外运行,可能会出错,也可能不会,其结果未定义。原因是SetName操作的name_变量在startup__产生时可能尚未产生。
解决方案是在名字空间中加入一个Mayers Singleton式的访问器来访问之
namespace {
string& GetName() {
static string name_;
return name_;
}
}
但是就namespace而言,你不得不对每个变量产生一个访问器,因为这些变量位于namespace而非class内,你无法产生一个类对象来统一使用这些变量。
如果是采用class+static的话,那么MonostateMixSingleton正是上述问题的解决方案。
结论
个人认为如果代码是写给自己用,那么Singleton是不错的选择,因为其实作简捷。另外在一些用到单件的设计模式中(比如State,Strategy等),标准用法也是Singleton(GOF就是这样用的)。
如果是写程序库,那么应该用MonostateMixSingleton,虽然其实现较为复杂,但具有易用的接口(不需要像Singleton那样每次都要调用Instance()),同时也没有全局变量初始化未定义的问题。
SingletonHolder其实就是Loki的做法,注意它并没有把目标类型变成单件,只是产生了目标类型的一个static变量而已。如果你不想自己实现Singleton并且有现成的Loki库使用,那么用这个方法也未尝不可。另外此法也没有全局变量初始化相依的问题。
补充
对于class+static的方式,你或许会将static放入实现文件中以匿名的namespace取代之,这样在头文件中的信息会隐藏的更深些。看其来是个不错的想法,但是 class+匿名namespace 的做法显然还未流行起来,鲜见于任何文献。为了避免你或者其他人产生混淆(怎么会有这么奇怪的东西?),建议你还是保持class+static的方式。
参考文献
《Effective C++第三版》chapter4
《C++设计新思维》chapter6
《敏捷软件开发》chapter16
#include <string>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
class Singleton {
public:
static Singleton& Instance() {
static Singleton instance;
return instance;
}
void SetName( string name ) { name_ = name; }
void ShowName() { cout << name_ << endl; };
private:
string name_;
Singleton() {}
~Singleton() {}
Singleton( const Singleton& rhs );
Singleton& operator=( const Singleton& rhs );
};
////////////////////////////////////////////////////////////////////////////////
class MonostateFromMartin {
public:
void SetName( string name ) { name_ = name; }
void ShowName() { cout << name_ << endl; };
private:
static string name_;
};
string MonostateFromMartin::name_;
////////////////////////////////////////////////////////////////////////////////
class MonostateFromAlexandrescu {
public:
static void SetName( string name ) { name_ = name; }
static void ShowName() { cout << name_ << endl; };
private:
static string name_;
};
string MonostateFromAlexandrescu::name_;
////////////////////////////////////////////////////////////////////////////////
class MonostateMixSingleton {
public:
static void SetName( string name ) { Instance().name_ = name; }
static void ShowName() { cout << Instance().name_ << endl; };
private:
static MonostateMixSingleton& Instance() {
static MonostateMixSingleton instance;
return instance;
}
string name_;
};
////////////////////////////////////////////////////////////////////////////////
class MonostateComplex {
public:
static void SetName( string name ) { impl.SetName( name ); }
static void ShowName() { impl.ShowName(); };
private:
class MonostateComplex_ {
public:
void SetName( string name ) { name_ = name; }
void ShowName() { cout << name_ << endl; };
private:
string name_;
};
static MonostateComplex_ impl;
};
MonostateComplex::MonostateComplex_ MonostateComplex::impl;
////////////////////////////////////////////////////////////////////////////////
namespace {
string name_;
}
namespace NamespaceSinglton {
void SetName( string name ) { name_ = name; }
void ShowName() { cout << name_ << endl; };
}
////////////////////////////////////////////////////////////////////////////////
template < class T >
class SingletonHolder {
public:
static T& Instance() {
static T instance;
return instance;
}
};
////////////////////////////////////////////////////////////////////////////////
void main() {
{
Singleton::Instance().SetName( "Singleton" );
Singleton::Instance().ShowName();
}
{
MonostateFromMartin m1;
m1.SetName( "MonostateFromMartin" );
MonostateFromMartin m2;
m2.ShowName();
}
{
MonostateFromAlexandrescu::SetName( "MonostateFromAlexandrescu" );
MonostateFromAlexandrescu::ShowName();
}
{
MonostateMixSingleton::SetName( "MonostateMixSingleton" );
MonostateMixSingleton::ShowName();
}
{
MonostateComplex::SetName( "MonostateComplex" );
MonostateComplex::ShowName();
}
{
NamespaceSinglton::SetName( "NamespaceSinglton" );
NamespaceSinglton::ShowName();
}
{
SingletonHolder< const char* >::Instance() = "abc";
}
}
综合比较
除Singleton以外的做法习惯上我把他们叫做仿单件。
Singleton的做法是标准的单件做法,但是每次都要以Instance()调用,颇不方便。
MonostateFromMartin的做法可以让多个对象拥有同一份数据。
后面的做法除了MonostateMixSingleton外都是类namespace(class+static)的做法,不如最后的 NamespaceSinglton来得简捷。但有一个问题,如果你要在main之前利用启动码(《C++设计新思维》p204)调用其静态成员函数(或者名 字空间的函数)就会产生初始化顺序未定义的问题,就是函数被调用时相应的静态变量(或者名字空间变量)可能尚未产生。比如:
bool startup() {
NamespaceSinglton::SetName( "NamespaceSinglton" );
return true;
}
bool startup__ = startup();
上述代码若在main()以外运行,可能会出错,也可能不会,其结果未定义。原因是SetName操作的name_变量在startup__产生时可能尚未产生。
解决方案是在名字空间中加入一个Mayers Singleton式的访问器来访问之
namespace {
string& GetName() {
static string name_;
return name_;
}
}
但是就namespace而言,你不得不对每个变量产生一个访问器,因为这些变量位于namespace而非class内,你无法产生一个类对象来统一使用这些变量。
如果是采用class+static的话,那么MonostateMixSingleton正是上述问题的解决方案。
结论
个人认为如果代码是写给自己用,那么Singleton是不错的选择,因为其实作简捷。另外在一些用到单件的设计模式中(比如State,Strategy等),标准用法也是Singleton(GOF就是这样用的)。
如果是写程序库,那么应该用MonostateMixSingleton,虽然其实现较为复杂,但具有易用的接口(不需要像Singleton那样每次都要调用Instance()),同时也没有全局变量初始化未定义的问题。
SingletonHolder其实就是Loki的做法,注意它并没有把目标类型变成单件,只是产生了目标类型的一个static变量而已。如果你不想自己实现Singleton并且有现成的Loki库使用,那么用这个方法也未尝不可。另外此法也没有全局变量初始化相依的问题。
补充
对于class+static的方式,你或许会将static放入实现文件中以匿名的namespace取代之,这样在头文件中的信息会隐藏的更深些。看其来是个不错的想法,但是 class+匿名namespace 的做法显然还未流行起来,鲜见于任何文献。为了避免你或者其他人产生混淆(怎么会有这么奇怪的东西?),建议你还是保持class+static的方式。
参考文献
《Effective C++第三版》chapter4
《C++设计新思维》chapter6
《敏捷软件开发》chapter16
- Singleton单件和仿单件的各种做法探讨
- 跨越C#和C++的单件(SINGLETON)模式
- 回头看singleton(单件)
- 单件Singleton
- 单件Singleton
- singleton单件模式
- Singleton (单件模式)
- 单件模式(Singleton)
- singleton 单件模式
- 单件模式[Singleton]
- 单件模型 Singleton
- SingleTon单件模式
- 单件模式(Singleton)
- Singleton 单件模式
- 单件模式-Singleton
- Singleton单件模式
- 【单件模式-Singleton】
- 单件模式(Singleton)
- dos环境下修改ip地址
- 抽象工厂模式(Abstract Factory)
- 创建数据透视表
- [强文]有几个还活着?十年应用软件之路
- 编写自己的IDE
- Singleton单件和仿单件的各种做法探讨
- 要回校了
- PPT使用技巧
- 工厂方法(Factory Method)之追根究底
- 在.NET下多层架构企业管理系统的开发
- Hibernate.isInitialized方法的使用(表是使用多对多映射关系实例中的表):
- MINA 快速入门指南(收藏)
- UML 之 C++类图关系全面剖析
- 测试文章推荐-测试的目的应该是验证需求