单例模式在系统开发时很常见,一个系统中可能会有多个单例模式的运用!它比全局变量的好处?1.能控制生成时间,保证初始化顺序?2. 容易保证线程安全(全局变量需要多线程client单独控制并发,单例在类实现中已经做到,无需client再处理)?3. 减少函数模块之间的耦合,减少命名冲突,便于管理? 可能这些都是好处吧!
我的项目开发过程中,经常会把单例模式和简单工厂模式结合起来使用!比如:
class OPTSOLVER_API CGasElementFactory
{
public:
~CGasElementFactory();
static CGasElementFactory& GetInstance();
virtual LPVOID CreateElement( IN DWORD nID, IN tstring strName, IN PIPELINE_ELEMENT_TYPE emType );
protected:
CGasElementFactory();
CGasElementFactory(const CGasElementFactory&);
CGasElementFactory & operator = (const CGasElementFactory &);
};
比较重要的就是其中的GetInnstance函数了,这的实现是:
CGasElementFactory& CGasElementFactory::GetInstance()
{
static CGasElementFactory s_modelValidatorFactory;
return s_modelValidatorFactory;
}
也就是说现在一般都不使用那种静态指针变量的方式了。可能是考虑到资源的释放吧,不过我觉得即使开发人员在系统的最后忘了释放单例new的对象,在系统运行结束时操作系统会自动收回!反正都是运行期一直存在的。采用上面的方式可能看着更优雅!
可能有人注意到上面代码中有一个受保护的=操作重载,这是因为,如果有人写出如下的代码:
CGasElementFactory factory = CGasElementFactory::GetInstance();
单例可以拷贝的话,就违背了单例的意义!因此要把它设为受保护成员!拷贝构造也要保护,虽然这样使用的不多。至此,一个单例类基本就完成了,
具体到使用上就是CGasElementFactory::GetInstance().xxx(这里是CreateElement函数)()!!!
至于线程安全的,我直接拷贝网上别人的代码吧!
class Lock
{
private:
mutex mtex;
public:
Lock(mutex m) : mtex(m)
{
mtex.Lock();
}
~Lock()
{
mtex.Unlock();
}
};
class Singleton {
public:
static Singleton* getInstance();
//析构的时候释放资源~
~Singleton() {
if( (_instance != NULL)
delete _instance;
}
protected:
Singleton();
private:
static Singleton* _instance;
static mutex m;
}
Singleton *Singleton::_instance = NULL;
Singleton* Singleton::getInstance() {
//check 之前进行临界区加锁操作
//双重检验加锁
if(_instance == NULL ) {
Lock lock(m);
if( _instance == NULL) {
_instance = new Singleton();
}
}
return _instance;
}
因为加锁比较耗时,又因为_instance只在第一次使用(为NULL)的时候才会跑到锁里面,所以外层的if判断就阻止了每次都加锁的操作!
下面的单例代码参考muduo库用C++11里的线程函数实现。这里用到C++模板的SFINAE特性,singleton在程序结束时用自带的destroy()函数释放资源。其实费了这么大劲,也就是为了在singleton析构时能释放掉资源。但singleton一般都是从创建开始一直存在到程序运行结束的,程序结束时它申请的所有资源都被操作系统回收。后面的测试程序显示有内存泄露,不太理解,虽然从表面上看TestNoDestroy类析构是没有调用。
#include <thread>
#include <mutex>
#include <fstream>
#include <cassert>
using namespace std;
namespace detail
{
// This doesn't detect inherited member functions!
// http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions
template<typename T>
struct has_no_destroy
{
template <typename C> static char test(decltype(&C::no_destroy)); // or decltype in C++11
template <typename C> static int32_t test(...);
const static bool value = sizeof(test<T>(0)) == 1;
};
}
template<typename T>
class Singleton
{
public:
static T& instance()
{
call_once(ponce_, Singleton::init);
assert(value_ != NULL);
return *value_;
}
private:
Singleton();
~Singleton();
static void init()
{
value_ = new T();
if (!detail::has_no_destroy<T>::value)
{
::atexit(destroy);
}
}
static void destroy()
{
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
T_must_be_complete_type dummy; (void)dummy;
delete value_;
value_ = NULL;
}
private:
static std::once_flag ponce_;
static T* value_;
};
template<typename T>
once_flag Singleton<T>::ponce_;
template<typename T>
T* Singleton<T>::value_ = NULL;
下面是测试程序:
class Test
{
public:
Test()
{
printf("tid=%d, constructing %p\n", this_thread::get_id(), this);
}
~Test()
{
printf("tid=%d, destructing %p %s\n", this_thread::get_id(), this, name_.c_str());
}
const string& name() const { return name_; }
void setName(const string& n) { name_ = n; }
private:
string name_;
};
class TestNoDestroy
{
public:
// Tag member for Singleton<T>
void no_destroy();
TestNoDestroy()
{
printf("tid=%d, constructing TestNoDestroy %p\n", this_thread::get_id(), this);
}
~TestNoDestroy()
{
printf("tid=%d, destructing TestNoDestroy %p\n", this_thread::get_id(), this);
}
};
void threadFunc()
{
printf("tid=%d, %p name=%s\n",
this_thread::get_id(),
&Singleton<Test>::instance(),
Singleton<Test>::instance().name().c_str());
Singleton<Test>::instance().setName("only one, changed");
}
int main()
{
Singleton<Test>::instance().setName("only one");
thread t1(threadFunc);
t1.join();
printf("tid=%d, %p name=%s\n",
this_thread::get_id(),
&Singleton<Test>::instance(),
Singleton<Test>::instance().name().c_str());
Singleton<TestNoDestroy>::instance();
printf("with valgrind, you should see %zd-byte memory leak.\n", sizeof(TestNoDestroy));
}