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

C++ 實現基於類名的動態對象創建:模擬 MFC 運行時類機制

     編輯:關於C++

背景動機

在後台服務開發中,常需從數據庫加載配置並緩存,支持定時主動刷新與被動通知更新。通用框架通常提供 FetcherFactory::getFetcher<XXXFetcher>() 介面,**自動掃描項目中所有繼承自 Fetcher 的實現類並動態實例化**。核心問題:C++ 無原生反射,如何根據字符串類名動態創建對象?

MFC 運行時類機制核心原理

利用 靜態成員變量在程序啟動前構造 的特性,為每個需動態創建的類註冊一個 CRuntimeClass 描述對象,通過鏈表串聯,運行時遍歷鏈表按名查找並調用創建函數。

關鍵數據結構

class CObject; // 前向聲明

struct CRuntimeClass {
    const char* m_lpszClassName;  // 類名
    int m_nObjectSize;            // 對象大小
    CObject* (*m_pfnCreateObject)(); // 創建函數指針
    CRuntimeClass* m_pNextClass;  // 鏈表下一節點

    static CRuntimeClass* pFirstClass; // 鏈表頭

    static CObject* CreateObject(const char* className);
};

兩個核心宏

// .h 中聲明
#define DECLARE_DYNAMIC(class_name) \
public: \
    static CRuntimeClass class##class_name; \
    virtual CRuntimeClass* GetRuntimeClass() const;

// .cpp 中實現
#define IMPLEMENT_DYNAMIC(class_name) \
    char _lpsz##class_name[] = #class_name; \
    CObject* Create##class_name() { return new class_name; } \
    CRuntimeClass class_name::class##class_name = { \
        _lpsz##class_name, sizeof(class_name), Create##class_name, nullptr \
    }; \
    static CLASS_INIT _init_##class_name(&class_name::class##class_name); \
    CRuntimeClass* class_name::GetRuntimeClass() const { \
        return &class_name::class##class_name; \
    }

自動註冊機制

struct CLASS_INIT {
    CLASS_INIT(CRuntimeClass* pNewClass) {
        pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
        CRuntimeClass::pFirstClass = pNewClass;
    }
};

每個類的 .cpp 編譯單元中定義一個 CLASS_INIT 靜態對象,其構造函數在 `main` 前執行,將該類的 CRuntimeClass 節點插入全局鏈表頭部。

創建對象流程

CObject* CRuntimeClass::CreateObject(const char* name) {
    for (CRuntimeClass* p = pFirstClass; p; p = p->m_pNextClass) {
        if (strcmp(p->m_lpszClassName, name) == 0) {
            return p->m_pfnCreateObject();
        }
    }
    return nullptr; // 未找到
}

使用示例

// 基類
class CObject {
    DECLARE_DYNAMIC(CObject)
public:
    virtual ~CObject() = default;
};
IMPLEMENT_DYNAMIC(CObject)

// 派生類
class CString : public CObject {
    DECLARE_DYNAMIC(CString)
public:
    CString() = default;
};
IMPLEMENT_DYNAMIC(CString)

// 客戶代碼
int main() {
    const char* className = "CString"; // 來自配置文件
    CObject* obj = CRuntimeClass::CreateObject(className);
    if (obj) {
        // 使用 obj...
        delete obj;
    }
    return 0;
}

工程實踐要點

  • 命名規約:實現類必須以特定後綴結尾(如 `Fetcher`),便於工廠識別
  • 宏分離聲明實現:`.h` 用 `DECLARE`,`.cpp` 用 `IMPLEMENT`,避免重複註冊
  • 防止同名衝突:項目內所有參與動態創建的類名必須唯一
  • 內存管理:工廠返回裸指針,調用方負責釋放;現代 C++ 可改用 std::unique_ptr 封裝

現代 C++ 替代方案

C++17 起可結合 std::any、std::function、std::unordered_map<string, Creator> 實現無宏的工廠註冊,配合 inline 靜態變量實現頭文件註冊,更類型安全且易調試。

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