C++如何实现获取类内部所有指定类型的子成员对象
实现方案
标准C++没有内置的反射能力来直接遍历类的成员变量,无法原生实现完全通用的GetObjects<T>()接口,但可以通过以下几种工程化方案实现「新增Command子类实例不需要修改打印逻辑」的需求:
方案1:内置容器统一注册(无第三方依赖,兼容性最好,最推荐)
思路是在App类中维护一个Command指针的容器,构造阶段将所有Command子类成员的指针注册到容器中,后续直接遍历容器即可。新增Command成员时仅需补充一行注册代码,无需修改打印逻辑。
注意:你原代码中子类重复定义了Name和Description成员,会隐藏基类的同名成员,导致基类指针访问时拿到空值,以下代码修正了该问题。
#include <vector> #include <iostream> #include <string> class Command { public: std::string Name; std::string Description; virtual void Compute() = 0; virtual ~Command() = default; // 基类添加虚析构避免内存泄漏 }; class StartCommand : public Command { public: StartCommand() { Name = "start"; Description = "Start the process"; } void Compute() override { // 启动进程逻辑 } }; class StopCommand : public Command { public: StopCommand() { Name = "stop"; Description = "Stop the process"; } void Compute() override { // 停止进程逻辑 } }; class App { public: StartCommand startCommand; StopCommand stopCommand; App() { // 新增Command成员仅需在此处添加一行注册代码 m_commands.push_back(&startCommand); m_commands.push_back(&stopCommand); } // 你需要的泛型接口实现 template<typename T> std::vector<T*> GetObjects() { std::vector<T*> result; for (auto cmd : m_commands) { if (auto typedPtr = dynamic_cast<T*>(cmd)) { result.push_back(typedPtr); } } return result; } void PrintAvailableCommands() { std::cout << "All available commands are: " << std::endl; for (auto cmd : GetObjects<Command>()) { std::cout << cmd->Name << ": " << cmd->Description << std::endl; } } private: std::vector<Command*> m_commands; };
方案优点:
- 无任何第三方依赖,所有C++版本均支持
- 逻辑简单易懂,问题排查成本低
- 完全满足需求,新增Command仅需补充一行注册代码
方案2:编译期静态反射(无需手动注册)
如果你的项目允许使用C++17及以上版本,可以借助仅头文件的Boost.PFR库实现完全无手动注册的GetObjects<T>()接口,编译期自动遍历App类的所有成员,筛选符合类型的对象。
#include <boost/pfr.hpp> #include <vector> #include <iostream> #include <string> #include <type_traits> // Command、StartCommand、StopCommand类定义同方案1 class App { public: StartCommand startCommand; StopCommand stopCommand; template<typename T> std::vector<T*> GetObjects() { std::vector<T*> result; // 编译期遍历App的所有成员字段 boost::pfr::for_each_field(*this, [&result](auto& field) { // 筛选继承自T的成员 if constexpr (std::is_base_of_v<T, std::decay_t<decltype(field)>>) { result.push_back(&field); } }); return result; } void PrintAvailableCommands() { std::cout << "All available commands are: " << std::endl; for (auto cmd : GetObjects<Command>()) { std::cout << cmd->Name << ": " << cmd->Description << std::endl; } } };
方案优点:
- 完全不需要手动注册,新增Command成员直接定义即可,无需修改其他代码
- 所有逻辑在编译期完成,无额外运行时开销
方案限制: - 要求C++17及以上版本,且App类必须为聚合类(无自定义构造函数、无私有非静态成员等)
- 依赖Boost.PFR库,仅头文件集成,无需编译Boost
内容的提问来源于stack exchange,提问作者Maxime Charrière
相关产品推荐
相关产品推荐

