在後台服務開發中,常需從數據庫加載配置並緩存,支持定時主動刷新與被動通知更新。通用框架通常提供 FetcherFactory::getFetcher<XXXFetcher>() 介面,**自動掃描項目中所有繼承自 Fetcher 的實現類並動態實例化**。核心問題:C++ 無原生反射,如何根據字符串類名動態創建對象?
利用 靜態成員變量在程序啟動前構造 的特性,為每個需動態創建的類註冊一個 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;
}
std::unique_ptr 封裝C++17 起可結合 std::any、std::function、std::unordered_map<string, Creator> 實現無宏的工廠註冊,配合 inline 靜態變量實現頭文件註冊,更類型安全且易調試。