请重点关注三和四,boost库实现的C++单例模板类。
一、简单实现(不推荐)
// C++单例模式测试程序
//
#include "stdafx.h"
#include <iostream>
using namespace std;
class CSingleton
{
private:
CSingleton(){;}//单例模式,禁止外部实例化
public:
//获取静态对象指针
static CSingleton *getInstance()
{ if (NULL == s_pInstance)
{
std::cout<<"new\n";
s_pInstance = new CSingleton();
}
return s_pInstance;
}
private:
static CSingleton *s_pInstance;
//类CFree被定义为CSingleton的私有内嵌类,以防该类被在其他地方滥用。它的唯一作用就是在析构函数中删除CSingleton的实例。
//程序运行结束时,系统会调用CSingleton的静态成员CFree的析构函数,该析构函数会删除单例的唯一实例。
class CFree
{
public:
~CFree()
{
if (CSingleton::s_pInstance != NULL)
{
std::cout<<"delete\n";
delete CSingleton::s_pInstance;
CSingleton::s_pInstance = NULL;
}
}
};
//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数
static CFree s_Fe;
};
CSingleton* CSingleton::s_pInstance = NULL;
CSingleton::CFree CSingleton::s_Fe;//类里面的静态变量要在类外部定义,如果没有这句话,程序结束时就不会运行CFree的析构函数.
int _tmain(int argc, _TCHAR* argv[])
{
CSingleton::getInstance();
return 0;
}
以上方法是可以编译正常运行的,析构函数也能如期执行。缺点是:1、线程不安全,多线程情况下会重复调用初始化函数;2、没有使用模板,数据类型不通用。所以不推荐使用。
二、模板实现1(不推荐)
我想采用C++模板的方式,重新改写了一下:
Singleton.h文件如下
#pragma once
template<typename T>
class CSingleton
{
public:
static T& getInstance()
{
if (NULL == s_pInstance)
{
std::cout << "new\n";
s_pInstance = new T();
}
return *s_pInstance;
}
private:
CSingleton();//单例模式,禁止外部实例化
~CSingleton();
CSingleton(const CSingleton&);
CSingleton& operator=(const CSingleton&);
private:
static T* s_pInstance;
//类CFree被定义为CSingleton的私有内嵌类,以防该类被在其他地方滥用。它的唯一作用就是在析构函数中删除CSingleton的实例。
//程序运行结束时,系统会调用CSingleton的静态成员CFree的析构函数,该析构函数会删除单例的唯一实例。
class CFree
{
public:
~CFree()
{
if (T* CSingleton<T>::s_pInstance != NULL)
{
std::cout << "delete\n";
delete T* CSingleton<T>::s_pInstance;
T* CSingleton<T>::s_pInstance = NULL;
}
}
};
//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数
static CFree s_Fe;
};
template<typename T> T* CSingleton<T>::s_pInstance = NULL;
template <typename T> typename CSingleton<T>::CFree CSingleton<T>::s_Fe;
main.cpp文件如下
#include <iostream>
#include "Singleton.h"
class A
{
public:
A();
~ A();
public:
void init()
{
std::cout << "hello world\n";
}
};
A:: A()
{
}
A::~ A()
{
}
int main()
{
CSingleton<A>::getInstance().init();
return 0;
}
结论是,这样子编译可以正常通过。但是有个问题是单例模板的内部类,析构函数没有执行。原因未知,这是个遗留问题,待解决!目前只能通过手动释放内存来解决。
static void destroy()
{
delete s_pInstance;
}
三、模板实现2(可行)
//C++设计模式——一个基于C++11的单例模板类
//https://blog.csdn.net/godmaycry/article/details/78458329
#include <memory>
#include <mutex>
template <typename T>
class CSingleton
{
public:
//获取全局单例对象
template <typename... Args>
static std::shared_ptr<T> GetInstance(Args &&... args)
{
if (!s_pSelf)
{
std::lock_guard<std::mutex> lock(s_mutex);
if (nullptr == s_pSelf)
{
s_pSelf = std::make_shared<T>(std::forward<Args>(args)...);
}
}
return s_pSelf;
}
//主动析构单例对象(一般不需要在外部主动调用该函数,除非特殊需求)
static void DesInstance()
{
if (s_pSelf)
{
s_pSelf.reset();
s_pSelf = nullptr;
}
}
private:
explicit CSingleton() {}
~CSingleton() {}
//禁止拷贝
CSingleton(const CSingleton &) = delete;
CSingleton &operator=(const CSingleton &) = delete;
CSingleton(CSingleton &&) = delete;
CSingleton &operator=(CSingleton &&) = delete;
private:
static std::shared_ptr<T> s_pSelf;
static std::mutex s_mutex;
};
template <typename T>
std::shared_ptr<T> CSingleton<T>::s_pSelf = nullptr;
template <typename T>
std::mutex CSingleton<T>::s_mutex;
使用举例说明:
#include "singleton.h"
CSingleton<CToolBox>::GetInstance()->myfun();
四、来自boost C++库的实现方式(推荐)
boost库实现线程安全,避免重复初始化 --- 推荐使用
boost库有多张单例的实现,分散在不同的代码里,能够独立摘出来用的主要有以下四个:
- boost/container/detail/singleton.hpp
- boost/serialization/singleton.hpp
- boost/thread/detail/singleton.hpp
- boost/pool/singleton_pool.hpp
尤其以前两个为主。我这里只讲解第一个,\boost_1_69_0\boost\container\detail\singleton.hpp原生的代码是这样的:
// boost/container/detail/singleton.hpp
// T must be: no-throw default constructible and no-throw destructible
template <typename T>
struct singleton_default
{
private:
struct object_creator
{
// This constructor does nothing more than ensure that instance()
// is called before main() begins, thus creating the static
// T object before multithreading race issues can come up.
object_creator() { singleton_default<T>::instance(); }
inline void do_nothing() const { }
};
static object_creator create_object;
singleton_default();
public:
typedef T object_type;
// If, at any point (in user code), singleton_default<T>::instance()
// is called, then the following function is instantiated.
static object_type & instance()
{
// This is the object that we return a reference to.
// It is guaranteed to be created before main() begins because of
// the next line.
static object_type obj;
// The following line does nothing else than force the instantiation
// of singleton_default<T>::create_object, whose constructor is
// called before main() begins.
create_object.do_nothing();
return obj;
}
};
template <typename T>
typename singleton_default<T>::object_creator
singleton_default<T>::create_object;
下面做一下中文讲解,出于习惯,我把:struct singleton_default名字修改成了class CSingleton。
其实注释已经说得很清楚了,这个实现使用了一个struct object_creator来作为类的static成员变量,单例的实体是obj,并不暴露出来,只是作为类的成员函数的static变量(local static对象)。通过create_object在main之前被执行构造来保证单例是在main之前被构造好。这个单例是利用main调用之前,程序只有一个线程,来保证单例在多线程下的唯一性。
#pragma once
template <typename T>
class CSingleton
{
private:
struct object_creator
{
object_creator()
{
CSingleton<T>::instance();
}
inline void do_nothing()const {}
};
//利用类的静态对象object_creator的构造初始化,在进入main之前已经调用了instance,
//从而避免了多次初始化的问题
static object_creator create_object;
public:
typedef T object_type;
static object_type& instance()
{
static object_type obj;
//据说这个do_nothing是确保create_object构造函数被调用
//这跟模板的编译有关
//do_nothing 是必要的,do_nothing的作用有点意思,
//如果不加create_object_.do_nothing();这句话,在main函数前面,create_object_的构造函数都不会被调用,instance当然也不会被调用,
//我的估计是模版的延迟实现的特效导致,如果没有这句话,编译器也不会实现Singleton<T>::object_creator,所以就会导致这个问题
create_object.do_nothing();
return obj;
}
private:
CSingleton();//单例模式,禁止外部实例化
~CSingleton();
CSingleton(const CSingleton&);
CSingleton& operator=(const CSingleton&);
};
//因为create_object是类的静态变量,必须有一个通用的声明
template <typename T> typename CSingleton<T>::object_creator CSingleton<T>::create_object;
使用举例说明:main.cpp
#include "stdafx.h"
#include <iostream>
#include "singleton.h"
class QMManager
{
//友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员),
//当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类.
friend class CSingleton<QMManager>;
protected:
QMManager() {};//这里必须写{},否则会编译出错.error LNK2019: 无法解析的外部符号 "protected: __thiscall QMManager::QMManager(void)" (??0QMManager@@IAE@XZ)
~QMManager() {};
public:
void do_something()
{
std::cout << "hello world";
};
};
int main()
{
CSingleton<QMManager>::instance().do_something();
return 0;
}
注意事项
所谓的单例仅仅是针对单个exe或者单个dll文件而言。如果某个单例类同时被exe和dll调用,要注意,exe和dll会各自实例化这个单例类。它们是各自独立的。所以需要共享的数据应该定义为静态成员变量。
参考文献
www.jianshu.com/p/333d4b37d… boost中的单例模式(singleton)
coolshell.cn/articles/26… 深入浅出单实例SINGLETON设计模式