一种C++ 单例万能模板类的实现和使用

背景

        首先,老生常谈一下单例模式,全局同时只允许存在一个单例对象(谨记),通过私有构造函数,提供静态的获取类实例对象接口的方式实现,分为饿汉和懒汉两种方式。饿汉:使用到的时候再通过接口去实例化(节省空间,线程不安全如多个线程同时构造此单例时,会出现多个实例);懒汉:初始化时已经创建实例,接口只用来返回(浪费内存,无线程安全问题)。但实际项目中大多场景会使用饿汉,并用的方法是解决线程安全问题。

        但是一个类在声明为单例的时候,其自身已被限制为单例类,无法具有普通类赋值拷贝等操作,在具体项目中使得声明为单例的功能类使用起来十分不灵活(比如某些处于逻辑中间的功能类,在某种情况下上层对其有部分单例类使用的需求,但也可作为一般功能类使用),此时直接声明为单例就很不方便。

        本文在工作项目的背景下设计一个单例模版本类,在编写特殊场景类时只需要声明此单例模板类为友元类,就可同时把此类当作普通类使用和部分功能单例类使用,同时使用此单例友元对象来管理类对象的生命周期,可以防止相同类对象多次析构的问题,具体见后面实例。

单例模板类实现

        此单例采用饿汉的方式,同时引入C++11的std::once_flag对象来保证全局只存在一份此单例类,保证线程安全,具体实现见 Sington.hpp:

/*
* @file  Sington.hpp
* @brief 单例模板类
*/

#ifndef LM_SINGTON_H_
#define LM_SINGTON_H_

#include <mutex>

template<typename T>
class CSington
{
public:
	using pointer = T*;

	// 禁止空构造 拷贝复制
	CSington() = delete;
	CSington(const CSington&) = delete;
	CSington& operator=(const CSington&) = delete;

	static T* instance()
	{
		std::call_once(m_instantiated, []() {
			pInstance = new T;
			});
		return pInstance;
	}

	static void destory()
	{
		if (pInstance)
		{
			delete pInstance;
			pInstance = nullptr;
		}
	}

private:
	static T* pInstance;
	static std::once_flag m_instantiated;
};

template<typename T>
T* CSington<T>::pInstance = nullptr;

template<typename T>
std::once_flag CSington<T>::m_instantiated;

#endif // !LM_SINGTON_H_

饿汉单例模式的实现此处不再啰嗦,重点说说C++11的std::once_flag和std::call_once。

        C++11的std::once_flag和std::call_once的组合使用可达到当前对象只需要初始化一次,可完美处理饿汉模式的线程不安全问题。

首先我们来看一下他两在std中的声明:

struct once_flag
{
    constexpr once_flag() noexcept;
    once_flag(const once_flag&) = delete;
    once_flag& operator=(const once_flag&) = delete;
};
template<class Callable, class ...Args>
  void call_once(once_flag& flag, Callable&& func, Args&&... args);
 
} // std

        可以看到once_flag是不允许修改的,拷贝构造函数和operator=函数都声明为delete,这样防止程序员乱用。另外,call_once也是很简单的,只要传进一个once_flag,回调函数,和参数列表就可以了。实际上once_flag相当于一个锁,使用它的线程都会在上面等待,只有一个线程允许执行。如果该线程抛出异常,那么从等待中的线程中选择一个,重复上面的流程。具体的细节像我这种菜鸟目前还不是很能懂,推荐大家去看大佬的博客

单例模板使用样例

        在定义的功能类时,只需要把套用此模板的单例类申明为友元类,包含此功能类模板的单例类就可以当作单例类来使用,非常方便,详见 MainApp.h

/*
* @file:  MainApp.h
* @brief:.主程序驱动类
*/

#ifndef LM_MAINAPP_H_
#define LM_MAINAPP_H_

#include <iostream>
#include <string>
#include <memory>
#include "MzmProjectImp.h"
#include "Sington.hpp"

class MainApp
{
	friend class CSington<MainApp>;  // 关键
public:
	MainApp(const MainApp&) = delete;
	MainApp& operator=(const MainApp&) = delete;
	~MainApp();

	// 初始化运行环境,加载插件等
	static bool init();

	// 反初始化
	static void uninit();

private:
	MainApp();

	void appStartLog();

private:
	std::string m_appPath;
	std::string m_dateTime;

};

using CMainApp = CSington<MainApp>;

#endif // !LM_MAINAPP_H_

使用代码:

        


#include "MainApp.h"
#include <QApplication>
#include <QDebug>

int main(int argc, char* argv[])
{
	if (argc > 2 && "-cmd" != std::string(argv[1]))
	{
		qDebug() << "Invalid parameter";
		return 0;
	}

	bool withUi = (1 == argc);
	QApplication app(argc, argv);

	if (!withUi)
	{
		return 0;
	}

	if (!MainApp::init())
	{
		CMainApp::destory();
		return 0;
	}

	QObject::connect(&app, &QCoreApplication::aboutToQuit, []() {
		CMainApp::destory();
		});

	return app.exec();
}

        上面例子中CMainApp->instance()在init()函数中实现,当然对于CMainApp->instance()实例对象的使用例子中并没有提现,但这是我实战项目中截取出的代码,保证是没有问题,请童鞋们放心使用并及时指正问题。

面向对象程序设计课程作业 1. 请创建一个数据类型为T的链表类模板List,实现以下成员函数: 1) 默认构造函数List(),将该链表初始化为一个空链表(10分) 2) 拷贝构造函数List(const List& list),根据一个给定的链表构造当前链表(10分) 3) 析构函数~List(),释放链表中的所有节点(10分) 4) Push_back(T e)函数,往链表最末尾插入一个元素为e的节点(10分) 5) operator<<()友元函数,将链表的所有元素按顺序输出(10分) 6) operator=()函数,实现两个链表的赋值操作(10分) 7) operator+()函数,实现两个链表的连接,A=B+C(10分) 2. 请编写main函数,测试该类模板的正确性: 1) 用List模板定义一个List类型的模板类对象int_listB,从键盘读入m个整数,调用Push_back函数将这m个整数依次插入到该链表中;(4分) 2) 用List模板定义一个List类型的模板类对象int_listC,从键盘读入n个整数,调用Push_back函数将这n个整数依次插入到该链表中;(4分) 3) 用List模板定义一个List类型的模板类对象int_listA,调用List的成员函数实现A = B + C;(4分) 4) 用cout直接输出int_listA的所有元素(3分) 5) 用List模板定义List类型的模板类对象double_listA, double_listB, double_listC,重复上述操作。(15分) 3. 输入输出样例: 1) 输入样例 4 12 23 34 45 3 56 67 78 3 1.2 2.3 3.4 4 4.5 5.6 6.7 7.8 2) 输出样例 12 23 34 45 56 67 78 1.2 2.3 3.4 4.5 5.6 6.7 7.8
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值