C++反射机制实现:结合模板类的实现

本文介绍了如何在C++中利用模板和反射机制实现一个简单易扩展的状态机工厂。通过定义宏ISTATEBASE_META_OBJECT,使得类在注册时只需关注这一宏,减少了额外代码。工厂类CFSMObjectCreator使用模板方法注册和注销对象,并通过单例模式提供全局访问。创建对象时,工厂会根据类名动态创建实例。
摘要由CSDN通过智能技术生成

背景

最近在实现基础的状态机逻辑,考虑到需要工厂来创建一个指定的状态机,而且调用要简单且可扩展,就想到了反射机制。

以前学习了文章:

https://blog.csdn.net/Scythe666/article/details/51718864

给的思路是通过定义回调函数指针类型的方法来实现的,也就是说反射出的对象需要显式地实现实例类创建的回调函数,然后在全局注册进去,虽然步骤已经很简单了,而且一个宏就搞定注册了。
不过我想要在扩展一个需反射对象时,对外暴露的“注册行为”更少,尽量减少一个对象实现时需要考虑过多的附带代码(毕竟拷贝多了很容易出错);另一个想法就是希望在用户调用注册时,不必在全局中实现,能够在任何地方的代码块中注册。

说到这里,充分考虑C++的各种支持,无疑多态跑不掉,毕竟有工厂的概念,不过为了能够把Create这个步骤交给内部处理,模板就发挥了它的效用了,也就是说,我只需要传类型进去,就可以实现注册了。


达到效果

在介绍实现之前,先放一个最终效果,也就是从用户扩展的角度,如何达到注册+创建的需求。

#include <stdlib.h>
#include <iostream>
#include <QApplication>
#include "../../FSMBase/Include/IStateBase.h"
#include "../../FSMBase/Include/FSMObjectCreator.h"

// IStateBase 就是我需要反射创建对象的基类
// CStateIDEL和CStateIDELNew是我测试扩展的子类对象,实现了两个

class CStateIDEL : public IStateBase
{
	ISTATEBASE_META_OBJECT("CStateIDEL")

public:
	CStateIDEL() {}
	virtual ~CStateIDEL() {}

	/*先省略一些与反射机制无关的接口实现*/
	
public:
	std::string m_strName;
};

class CStateIDELNew : public IStateBase
{
	ISTATEBASE_META_OBJECT("CStateIDELNew")

public:
	CStateIDELNew() {}
	virtual ~CStateIDELNew() {}

	/*先省略一些与反射机制无关的接口实现*/

public:
	std::string m_strName;
};

int main(int argc, char* argv[])
{
	QApplication app(argc, argv);

	CFSMObjectCreator* pCreator = CFSMObjectCreatorSingleton::GetInstance();

	pCreator->RegisterStateBaseType<CStateIDEL>();     // 注册扩展的 CStateIDEL类
	pCreator->RegisterStateBaseType<CStateIDELNew>();  // 注册扩展的 CStateIDELNew类


	CStateIDEL* pIDEL = dynamic_cast<CStateIDEL*>(pCreator->CreateStateBase("CStateIDEL", "111"));  // 创建 CStateIDEL类对象

	pCreator->UnregisterStateBaseType<CStateIDELNew>();    // 取消注册CStateIDELNew类
	CStateIDELNew* pIDELNew = dynamic_cast<CStateIDELNew*>(pCreator->CreateStateBase("CStateIDELNew", "222"));    // 创建CStateIDELNew类对象,但此时对象指针是空的

	app.exec(); 
	return 0;
}

实现过程

实例代码结构:
在这里插入图片描述
IStateBase就是我们需要反射对象的基类

  • ISTATEBASE_META_OBJECT宏是重点,需要向CFSMObjectCreator提供的反射类名(类名要对应Create),对于用户创建一个新的子类,唯一关注的也就是这里了,可以理解为,和Qt的Q_OBJECT加入以支持信号槽机制是一个道理。
  • Initialize和Uninitialize作为工厂调用初始化和反初始化的接口;
  • 其它的是其它业务接口类,可以忽略;
#include "FSMBaseExport.h"

#define ISTATEBASE_META_OBJECT(RefClass) \
public:\
	static std::string GetRefClass() {return RefClass;}

class IStateMachine;
class FSMBASE_EXPORT IStateBase
{
	ISTATEBASE_META_OBJECT("IStateBase")

public:
	virtual ~IStateBase() {}

	virtual bool Initialize(const std::string& strName) = 0;

	virtual void Uninitialize() = 0;

	virtual std::string GetName() const = 0;

	virtual bool Entry(IStateMachine* pFSM) = 0;

	virtual bool Execute(IStateMachine* pFSM) = 0;

	virtual bool Exit(IStateMachine* pFSM) = 0;
};

CFSMObjectCreator和CFSMObjectCreatorSingleton对外提供注册和反注册,创建和销毁IStateBase的单例工厂

  • 对于外部调用来说,如何获取该全局唯一的工厂是第一步,这里的一个方式是用CFSMObjectCreatorSingleton封装了一层CFSMObjectCreator提供出去。
  • 注意RegisterStateBaseType和UnregisterStateBaseType使用了模板关联反射类,这里就是把Create可以放到内部的关键。因为语法的原因,模板函数的实现写在了头文件内,不过m_mapClassName具体的insert和erase的逻辑是由RegisterType和UnregisterType封装了起来,只是单纯为了不把加锁的逻辑放在外面,仅在cpp内实现。
  • 每个类对应的Create是通过CObjectRegisterAngent代理提供的Create接口实现的。
  • std::map<std::string, CObjectRegisterAngent*> m_mapClassName; 用类名映射创建代理类实例。
class FSMBASE_EXPORT CFSMObjectCreator
{
public:
	
	CFSMObjectCreator();
	~CFSMObjectCreator();

	template <typename T>
	void RegisterStateBaseType()
	{
		CFSMObjectRegisterAngent<T>* pAngent = new CFSMObjectRegisterAngent<T>();
		std::string strClassName = pAngent->GetRefName();
		RegisterType(strClassName, pAngent);
	}

	template <typename T>
	void UnregisterStateBaseType()
	{
		std::string strClassName = T::GetRefClass();
		UnregisterType(strClassName);
	}

	IStateBase* CreateStateBase(const std::string& strClassName, const std::string& strName);

	void DestroyStateBase(IStateBase* pStateBase);

private:

	void RegisterType(const std::string& strClassName, CObjectRegisterAngent* pAngent);

	void UnregisterType(const std::string& strClassName);

private:

	// 有必要加锁
	std::map<std::string, CObjectRegisterAngent*> m_mapClassName;
};

// ================================================

class FSMBASE_EXPORT CFSMObjectCreatorSingleton
{
public:

	static CFSMObjectCreator* GetInstance();

private:
	CFSMObjectCreatorSingleton() {}
	~CFSMObjectCreatorSingleton() {}
};

最重点的实现:使用模板对应到Create

  • 刚才我们说了,CObjectRegisterAngent对应每个注册进来的反射类所需要的Create,但是还没有真正把模板类T给用上;
  • CFSMObjectRegisterAngent作为一个继承于CObjectRegisterAngent的模板类,就实现了virtual IStateBase* Create() = 0;的方法,而且对应到了注册进的类。
  • CFSMObjectRegisterAngent提供了一个GetRefName的方法,获取刚才用宏实现的IStateBase提供反射类名的方法;
  • 一个小细节:容器内没有办法使用模板类,所以必须使用CFSMObjectRegisterAngent的基类,也是多了一个CObjectRegisterAngent的主要原因。
class IStateBase;
class CObjectRegisterAngent
{
public:
	virtual ~CObjectRegisterAngent() {}

	virtual IStateBase* Create() = 0;
};

template <typename T>
class CFSMObjectRegisterAngent : public CObjectRegisterAngent
{
public:
	CFSMObjectRegisterAngent() {}
	~CFSMObjectRegisterAngent() {}

	IStateBase* Create()
	{
		return new T;
	}

	std::string GetRefName() const
	{
		return T::GetRefClass();
	}
};

CFSMObjectCreator内的实现过程
其实反射机制的主要内容都在CFSMObjectCreator的头文件内了,CPP内只是做了简单的m_mapClassName容器操作,其次是封装了锁,不暴露出去,也就是完成了一个最简化的工厂职责处理。
有兴趣可以参考下:

#include "../Include/FSMObjectCreator.h"
#include "../Include/IStateBase.h"

CFSMObjectCreator::CFSMObjectCreator()
{
	// 锁初始化
}

CFSMObjectCreator::~CFSMObjectCreator()
{
	// 锁销毁
}

IStateBase* CFSMObjectCreator::CreateStateBase(const std::string& strClassName, const std::string& strName)
{
	// 加锁
	std::map<std::string, CObjectRegisterAngent*>::const_iterator itr = m_mapClassName.find(strClassName);
	if (itr == m_mapClassName.end())
	{
		return NULL;
	}

	IStateBase* pStateBase = itr->second->Create();
	if (NULL == pStateBase)
	{
		return NULL;
	}

	bool bInit = pStateBase->Initialize(strName);
	if (!bInit)
	{
		delete pStateBase;
		return NULL;
	}

	return pStateBase;
}

void CFSMObjectCreator::DestroyStateBase(IStateBase* pStateBase)
{
	if (NULL == pStateBase)
	{
		return;
	}
	pStateBase->Uninitialize();
	delete pStateBase;
}

// =====

void CFSMObjectCreator::RegisterType(const std::string& strClassName, CObjectRegisterAngent* pAngent)
{
	// 加锁
	std::map<std::string, CObjectRegisterAngent*>::iterator itr = m_mapClassName.find(strClassName);
	if (itr == m_mapClassName.end())
	{
		m_mapClassName.insert(std::make_pair(strClassName, pAngent));
	}
	else
	{
		// LOG ??
		delete itr->second;
		itr->second = pAngent;
	}
}

void CFSMObjectCreator::UnregisterType(const std::string& strClassName)
{
	// 加锁
	std::map<std::string, CObjectRegisterAngent*>::const_iterator itr = m_mapClassName.find(strClassName);
	if (itr == m_mapClassName.end())
	{
		return;
	}
	delete itr->second;
	m_mapClassName.erase(itr);
}

// ======= Singleton

CFSMObjectCreator* CFSMObjectCreatorSingleton::GetInstance()
{
	static CFSMObjectCreator g_FSMObjCreator;
	return &g_FSMObjCreator;
}
面向对象程序设计课程作业 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、付费专栏及课程。

余额充值