首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

C++ 反射機制手動實現:工廠模式、註冊表與字符串創建對象

     編輯:C++ 入門

背景:C++ 無原生反射

Java/C# 通過反射實現「字符串類名 → 對象實例」。C++ 編譯期類型信息極少 (RTTI 僅支持 typeid/dynamic_cast),無法直接按名創建對象。解決方案:**手動註冊表 + 工廠模式**。

核心架構:DynBase 基類 + ClassInfo 註冊項

1. 註冊項結構


#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); // 構造時自動註冊
    }
};

2. 基類註冊表 (Meyers Singleton 保證初始化順序)


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;
    }
};

3. 具體類實現:繼承 DynBase + 靜態註冊實例


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);

4. 使用示例


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 保證函數局部靜態初始化原子性;註冊發生在靜態初始化期 (單線程)

現代替代:C++20 std::factory / 反射提案 (P2996)

C++ 標準委員會正在推進靜態反射提案 (P2996),未來可編譯期獲取類成員、構造函數、註解等,實現零開銷自動序列化/反序列化/工廠生成。目前仍需手動實現或使用第三方庫 (如 RTTR, boost::pfr, magic_enum)。

總結

C++ 手動反射核心模式:**基類註冊表 (Map) + 靜態註冊實例 (自動執行註冊) + 工廠方法 (按名創建)**。關鍵在於解決靜態初始化順序 (Meyers Singleton)、所有權語義 (unique_ptr)、類型擦除 (function)。此模式廣泛用於插件系統、腳本綁定、配置驅動對象創建、網絡協議解碼等場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved