C++ 仿 MFC 反射系统实现:支持动态创建与成员函数调用
一、背景
在 C++ 开发中,反射(Reflection)是一项强大但语言本身不支持的特性。许多框架(如 MFC、Qt)通过各自的宏和运行时类型信息(RTTI)机制实现了类似反射的功能。本文参考 MFC 的 CRuntimeClass 设计思想,实现一套轻量级、可复用的反射系统,具备以下能力:
- 通过类名字符串动态创建对象。
- 运行时类型判断(
IsKindOf)。 - 支持带任意参数和返回值的成员函数动态调用 (使用
std::any类型擦除)。 - 宏自动化注册,无需手动编写重复代码。
完整代码基于标准 C++17,不依赖 Qt 等大型框架,可直接集成到现有项目中。
二、设计思路
2.1 运行时类信息(CRuntimeClass)
每个需要反射的类都对应一个静态 CRuntimeClass 对象,存储:
- 类名(
m_lpszClassName) - 基类运行时信息指针(
m_pBaseClass) - 创建对象的函数指针(
m_pfnCreateObject) - 方法调用表(
m_methods)
通过 CRuntimeClass 可以创建对象、判断继承关系、动态调用方法。
2.2 反射工厂(CRttiFactory)
单例模式管理所有已注册类的 CRuntimeClass 指针。提供:
RegisterClass:注册类信息(由宏自动调用)。CreateObject:根据类名查找并创建对象。GetRegisteredClassNames:获取所有类名(调试用)。
2.3 成员函数反射(MethodInvoker)
使用 std::any 统一处理任意类型的参数和返回值。每个成员函数被包装成一个 MethodInvoker 类型的可调用对象:
cpp
std::any(CRttiObject*, const std::vector<std::any>&)
内部通过可变参数模板和 std::index_sequence 解析参数列表并调用真实函数。
2.4 宏自动化
借鉴 MFC 的 DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC 宏,自动生成静态 CRuntimeClass 对象和注册代码。成员函数反射也通过宏声明和注册。
三、核心代码解析
3.1 类与结构定义
cpp
// 创建对象函数类型
using ObjectConstructor = std::function<CRttiObject*()>;
// 方法调用器类型
using MethodInvoker = std::function<std::any(CRttiObject*, const std::vector<std::any>&)>;
// 方法元信息
class CMethodInfo {
public:
const char* name;
MethodInvoker invoker;
CMethodInfo(const char* n, MethodInvoker i) : name(n), invoker(std::move(i)) {}
};
// 运行时类信息
class CRuntimeClass {
public:
const char* m_lpszClassName;
CRuntimeClass* m_pBaseClass;
ObjectConstructor m_pfnCreateObject;
CRuntimeClass(const char* className, CRuntimeClass* baseClass, ObjectConstructor ctor);
CRttiObject* CreateObject() const;
bool IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
// 方法管理
void RegisterMethod(const char* name, MethodInvoker invoker);
std::any InvokeMethod(CRttiObject* obj, const char* name, const std::vector<std::any>& args = {}) const;
bool HasMethod(const char* name) const;
private:
std::unordered_map<std::string, CMethodInfo> m_methods;
};
3.2 反射基类与工厂
cpp
// 所有反射类的基类
class CRttiObject {
public:
virtual ~CRttiObject() = default;
virtual const CRuntimeClass* GetRuntimeClass() const = 0;
bool IsKindOf(const CRuntimeClass* pClass) const;
static CRttiObject* CreateObject(const char* className);
};
// 单例工厂(省略部分代码,详见文末源码)
class CRttiFactory {
// 线程安全注册与查找
std::unordered_map<std::string, CRuntimeClass*> m_classMap;
std::vector<CRuntimeClass*> m_vecRuntimeClasses;
mutable std::mutex m_mutex;
// ...
};
3.3 成员函数调用适配器
使用模板将任意成员函数转换为统一签名:
cpp
namespace ReflectDetail {
// 实际调用包装,通过 std::any_cast 提取参数
template<typename Class, typename Ret, typename... Args, std::size_t... I>
std::any InvokeImpl(Class* obj, Ret(Class::* func)(Args...),
const std::vector<std::any>& args, std::index_sequence<I...>) {
if (args.size() != sizeof...(Args))
throw std::runtime_error("Argument count mismatch");
if constexpr (std::is_void_v<Ret>) {
(obj->*func)(std::any_cast<std::decay_t<Args>>(args[I])...);
return std::any();
} else {
return std::any((obj->*func)(std::any_cast<std::decay_t<Args>>(args[I])...));
}
}
// 生成 MethodInvoker
template<typename Class, typename Ret, typename... Args>
MethodInvoker MakeInvoker(Ret(Class::* func)(Args...)) {
return [func](CRttiObject* base, const std::vector<std::any>& args) -> std::any {
Class* obj = dynamic_cast<Class*>(base);
if (!obj) throw std::runtime_error("Invalid object type");
return InvokeImpl(obj, func, args, std::index_sequence_for<Args...>{});
};
}
// 同样提供 const 成员函数版本(略)
}
3.4 宏定义
类反射宏:
cpp
#define DECLARE_DYNAMIC(className) \
public: \
static CRuntimeClass class##className; \
virtual const CRuntimeClass* GetRuntimeClass() const override { return &class##className; } \
static CRttiObject* CreateObject() { return new className(); }
#define IMPLEMENT_ROOT_DYNAMIC(className) \
CRuntimeClass className::class##className(#className, nullptr, &className::CreateObject); \
static struct __Register_##className { \
__Register_##className() { CRttiFactory::Instance().RegisterClass(&className::class##className); } \
} __register_##className;
成员函数反射宏(支持0~4个参数):
cpp
#define DECLARE_REFLECT_METHOD(className, method) \
static void _RegisterMethod_##method();
#define IMPLEMENT_REFLECT_METHOD(className, method, ...) \
/* 通过参数个数选择具体宏,调用 MakeInvoker 并注册 */
宏自动展开后,会在 main 前执行注册代码,确保类和方法在运行时可用。
四、使用示例
4.1 定义反射类
TestReflect.h
cpp
#pragma once
#include "ReflectClass.h"
class CTestReflect : public CRttiObject
{
DECLARE_DYNAMIC(CTestReflect)
DECLARE_REFLECT_METHOD(CTestReflect, SetValue)
DECLARE_REFLECT_METHOD(CTestReflect, GetValue)
public:
CTestReflect() : m_nValue(2) {}
virtual ~CTestReflect() {}
void SetValue(int nValue, float fValue) { m_nValue = nValue; m_fValue = fValue; }
int GetValue() { return m_nValue; }
private:
int m_nValue;
float m_fValue;
};
4.2 实现注册
TestReflect.cpp
cpp
#include "TestReflect.h"
IMPLEMENT_ROOT_DYNAMIC(CTestReflect)
IMPLEMENT_REFLECT_METHOD(CTestReflect, SetValue, int, float)
IMPLEMENT_REFLECT_METHOD(CTestReflect, GetValue)
4.3 动态调用
Main.cpp
cpp
#include "TestReflect.h"
#include <iostream>
int main()
{
CRttiObject* obj = CRttiObject::CreateObject("CTestReflect");
if (obj) {
const CRuntimeClass* cls = obj->GetRuntimeClass();
// 带参数调用
std::vector<std::any> args = { 10, 2.5f };
cls->InvokeMethod(obj, "SetValue", args);
// 带返回值调用
std::any ret = cls->InvokeMethod(obj, "GetValue", {});
if (ret.has_value()) {
int val = std::any_cast<int>(ret);
std::cout << "GetValue: " << val << std::endl; // 输出 10
}
delete obj;
}
return 0;
}

五、关键技术细节与注意事项
5.1 静态初始化顺序
- 使用局部静态单例(
CRttiFactory::Instance())确保工厂在首次访问时构造。 - 宏生成的注册类在
main前执行,但调用RegisterClass时工厂已构造,顺序安全。
5.2 类型严格匹配
std::any_cast 要求类型完全一致 ,包括 cv 限定符和引用。示例中 2.5f 显式指定为 float,若误写 2.5(double)将抛出 std::bad_any_cast。
5.3 函数重载处理
若存在同名重载函数,需通过 static_cast 指定确切签名,宏已包含此转换。
5.4 线程安全
工厂的 RegisterClass 和查询方法均加锁,可在多线程环境下安全使用。
5.5 扩展性
- 支持更多参数:参照现有宏模式增加
IMPLEMENT_REFLECT_METHOD_5等即可。 - 支持非 void 返回值:已有完整支持,无需额外处理。
六、完整代码获取
完整头文件 ReflectClass.h 可进q裙提供。项目结构如下:
├── ReflectClass.h # 反射核心实现
├── TestReflect.h/cpp # 示例反射类
└── Main.cpp # 测试入口
七、总结
本文实现了一套仿 MFC 的 C++ 反射系统,具备以下亮点:
- 轻量级:仅依赖 C++17 标准库,无第三方依赖。
- 功能全面:支持动态创建、继承判断、成员函数调用。
- 类型安全 :利用
std::any和模板推导,编译期保证类型正确。 - 易用性:宏自动化注册,对业务代码侵入极小。
这套反射机制可应用于插件系统、序列化/反序列化、脚本绑定、RPC 调用等场景,为 C++ 项目带来动态语言的灵活性。