Java/C# 通過反射實現「字符串類名 → 對象實例」。C++ 編譯期類型信息極少 (RTTI 僅支持 typeid/dynamic_cast),無法直接按名創建對象。解決方案:**手動註冊表 + 工廠模式**。
#include <string>
#include <map>
#include <functional>
#include <memory>
using CreateFunc = std::function()>; // 現代用 function+unique_ptr
struct ClassInfo {
std::string name;
CreateFunc creator;
ClassInfo(std::string n, CreateFunc c) : name(std::move(n)), creator(std::move(c)) {
DynBase::registerClass(this); // 構造時自動註冊
}
};
class DynBase {
public:
virtual ~DynBase() = default;
// 靜態工廠方法
static std::unique_ptr createObject(const std::string& name) {
auto& registry = getRegistry();
auto it = registry.find(name);
if (it != registry.end()) {
return it->second.creator(); // 調用創建函數
}
return nullptr;
}
protected:
// 供 ClassInfo 構造函數調用
static void registerClass(ClassInfo* info) {
getRegistry()[info->name] = *info; // 複製存儲 (或存指針/shared_ptr)
}
private:
// 函數局部靜態:首次調用初始化,線程安全 (C++11)
static std::map& getRegistry() {
static std::map registry;
return registry;
}
};
class IndustryOperate : public DynBase {
public:
IndustryOperate() { /* 初始化 */ }
~IndustryOperate() override = default;
void doWork() { /* 具體業務 */ }
// 靜態創建函數 (匹配 CreateFunc 簽名)
static std::unique_ptr createInstance() {
return std::make_unique();
}
// 關鍵:靜態 ClassInfo 實例,程序啟動前自動構造註冊
// 名稱 "IndustryOperate" 為鍵
static ClassInfo classInfo;
};
// 定義靜態成員 (在 .cpp 中)
ClassInfo IndustryOperate::classInfo("IndustryOperate", &IndustryOperate::createInstance);
int main() {
// 從配置文件/網絡/腳本讀取類名
std::string className = "IndustryOperate";
// 工廠創建
auto obj = DynBase::createObject(className);
if (obj) {
// 需下轉調用具體接口 (或定義虛函數接口)
if (auto* op = dynamic_cast(obj.get())) {
op->doWork();
}
} else {
std::cerr << "Unknown class: " << className << std::endl;
}
return 0;
}
#define REGISTER_CLASS(className) \
static ClassInfo className##_info(#className, &className::createInstance); \
static std::unique_ptr createInstance() { return std::make_unique(); }
// 使用
class MyClass : public DynBase {
REGISTER_CLASS(MyClass)
public:
void foo() {}
};
| 問題 | 解決方案 |
|---|---|
| 靜態初始化順序跨文件未定義 | Meyers Singleton (getRegistry() 函數局部靜態) 保證首次使用前初始化 |
| 對象所有權管理 | 工廠返回 std::unique_ptr,明確所有權轉移,避免洩漏 |
| 創建函數統一簽名 | std::function 統一不同類的構造 |
| 類型安全下轉 | 提供虛函數接口避免 dynamic_cast,或用 visitor 模式 |
| 線程安全註冊 | C++11 保證函數局部靜態初始化原子性;註冊發生在靜態初始化期 (單線程) |
std::factory / 反射提案 (P2996)C++ 標準委員會正在推進靜態反射提案 (P2996),未來可編譯期獲取類成員、構造函數、註解等,實現零開銷自動序列化/反序列化/工廠生成。目前仍需手動實現或使用第三方庫 (如 RTTR, boost::pfr, magic_enum)。
C++ 手動反射核心模式:**基類註冊表 (Map