在前端或者后台开发过程中,或多或少都要有一些全局变量,或者某个关键数据不允许重复构造,这个时候单例模式在C++程序中是一个不错的选择。
本代码示例结合智能指针做了一个单例模式。
class IwmGlobal : private std::enable_shared_from_this<IwmGlobal>
{
public:
IwmGlobal(const IwmGlobal &) = delete;
IwmGlobal &operator=(const IwmGlobal &) = delete;
static std::shared_ptr<IwmGlobal> &instance() {
if (nullptr != s_instance)
{
return s_instance;
}
Mutex::Locker lock(s_mutex);
if (nullptr != s_instance)
{
return s_instance;
}
s_instance = shared_ptr<IwmGlobal>(new IwmGlobal());
return s_instance;
}
private:
static std::shared_ptr<IwmGlobal> s_instance;
IwmGlobal() { m_bInit = FALSE; }
static Mutex s_mutex;
private:
BOOL m_bInit;
public:
//所有配置文件相关信息
std::shared_ptr<ConfigData> appCfg;
IchsPool<IWMBaseMsgPtr> taskManMsgQueue;
IchsPool<IWMBaseMsgPtr> robotCtrlQueue;
std::shared_ptr<crMioEx> mqHandle;
string listener_topic;
};
typedef std::shared_ptr<IwmGlobal> IwmGlobalPtr;
说明:
1、将构造函数声明为 private
private:
static std::shared_ptr<IwmGlobal> s_instance;
IwmGlobal() { m_bInit = FALSE; }
2、将C++类的默认实现的类重载函数去掉
IwmGlobal(const IwmGlobal &) = delete;
IwmGlobal &operator=(const IwmGlobal &) = delete;
3、初始化实例
static std::shared_ptr<IwmGlobal> &instance() {
if (nullptr != s_instance)
{
return s_instance;
}
Mutex::Locker lock(s_mutex);
if (nullptr != s_instance)
{
return s_instance;
}
s_instance = shared_ptr<IwmGlobal>(new IwmGlobal());
return s_instance;
}
因为单例模式无法直接调用make_shared()进行实例化,原因是构造函数为private,无法被外部调用,所以折中的方式是:
s_instance = shared_ptr<IwmGlobal>(new IwmGlobal());
注意:
因为是单例模式,相当于全局变量的存在,在程序加载过程中可能会存在instance多线程同时加载的情况, 为了避免该懒加载模式出现问题,最好加一个资源锁。
另外,单例模式设计过程需要明确区分,句柄初始化和数据加载初始化。
1、句柄初始化
句柄初始化为类或者结构体初始化,
2、数据加载初始化
而数据加载初始化是为了服务正常工作,需要加载配置数据或者连接初始化(如服务配置、tcp连接、服务初始化、db数据库连接初始化、MQ连接初始化、redis连接初始化)
个人理解,单例模式适合做句柄初始化,从程序架构上来看,最好将逻辑分开,一则污染了单例模式的设计本意,二则代码逻辑上对单例模式也是承重的负担。数据加载初始化建议采用工厂模式解耦处理。