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

MFC 核心類 CObject 詳解:運行時類型、動態創建與序列化機制

     編輯:關於VC++

前言:CObject 在 MFC 體系中的地位

CObject 是 MFC 絕大多數類別的根基類,提供了四大核心特性:運行時類型資訊(RTTI)、動態創建、序列化 以及 診斷輸出。理解其結構與實作機制,是掌握 MFC 架構設計精髓的關鍵。

一、CObject 類別結構剖析

class CObject {
public:
    virtual CRuntimeClass* GetRuntimeClass() const;
    virtual ~CObject();                       // 虛擬解構子
    void* PASCAL operator new(size_t nSize);  // 診斷用記憶體分配
    // ... 除錯版 new/delete 多載
protected:
    CObject();                                // 預設建構子
private:
    CObject(const CObject&);                 // 禁止複製建構
    void operator=(const CObject&);          // 禁止指派
public:
    BOOL IsSerializable() const;
    BOOL IsKindOf(const CRuntimeClass* pClass) const;
    virtual void Serialize(CArchive& ar);     // 序列化入口
    virtual void AssertValid() const;         // 診斷
    virtual void Dump(CDumpContext& dc) const;
    static const AFX_DATA CRuntimeClass classCObject; // 靜態類型資訊
#ifdef _AFXDLL
    static CRuntimeClass* PASCAL _GetBaseClass();
#endif
};

關鍵點:五個虛擬函式(解構子、GetRuntimeClass、Serialize、AssertValid、Dump)與 靜態成員 classCObject 共同支撐三大特性。

二、三大特性的啟用方式與層級關係

特性宣告巨集(.h)實作巨集(.cpp)必要條件
運行時類型DECLARE_DYNAMIC(Class)IMPLEMENT_DYNAMIC(Class, Base)—
動態創建DECLARE_DYNCREATE(Class)IMPLEMENT_DYNCREATE(Class, Base)需有預設建構子
序列化DECLARE_SERIAL(Class)IMPLEMENT_SERIAL(Class, Base, wSchema)需有預設建構子、覆寫 Serialize

層級遞增:序列化 ⊃ 動態創建 ⊃ 運行時類型。啟用高層特性自動包含低層功能。

三、巨集展開與 CRuntimeClass 核心結構

3.1 宣告巨集展開後的成員

  • DECLARE_DYNAMIC:增加靜態 CRuntimeClass classClassName、虛擬 GetRuntimeClass、(DLL 版)靜態 _GetBaseClass。
  • DECLARE_DYNCREATE:含上述全部,再增靜態 CreateObject。
  • DECLARE_SERIAL:含 _DECLARE_DYNCREATE,並將 classClassName 設為非 const、友元重載 operator>>、靜態 AFX_CLASSINIT _init_ClassName 確保模組啟動時註冊類型資訊。

3.2 CRuntimeClass 結構定義

struct CRuntimeClass {
    LPCSTR m_lpszClassName;           // 類別名稱字串
    int m_nObjectSize;                // 物件大小
    UINT m_wSchema;                   // 序列化版本號(0xFFFF 表不支援)
    CObject* (PASCAL* m_pfnCreateObject)(); // 動態創建函式指標
    CRuntimeClass* (PASCAL* m_pfnGetBaseClass)(); // 取得基類資訊
    CRuntimeClass* m_pBaseClass;      // 基類 CRuntimeClass 指標
    CRuntimeClass* m_pNextClass;      // 模組類別鏈表指標
    CObject* CreateObject();          // 封裝動態創建
    BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const; // 繼承關係判斷
};

3.3 三種層級下的初始化差異

成員運行時類型動態創建序列化
m_lpszClassName類別名類別名類別名
m_nObjectSizesizeof(Class)sizeof(Class)sizeof(Class)
m_wSchema0xFFFF0xFFFF1,2... (非 0)
m_pfnCreateObjectNULL&Class::CreateObject&Class::CreateObject
m_pfnGetBaseClass&Class::_GetBaseClass&Class::_GetBaseClass&Class::_GetBaseClass
m_pBaseClass&Base::classBase&Base::classBase&Base::classBase

CObject::classCObject 初始化為 {"CObject", sizeof(CObject), 0xFFFF, NULL, &CObject::_GetBaseClass, NULL}。

四、核心機制實作細節

4.1 動態創建流程

CObject* CRuntimeClass::CreateObject() {
    if (m_pfnCreateObject == NULL) { TRACE(...); return NULL; }
    CObject* pObject = NULL;
    TRY { pObject = (*m_pfnCreateObject)(); } END_TRY
    return pObject;
}

使用靜態成員函式指標繞過 C++ 無法直接以類別名稱創建物件的限制。

4.2 IsKindOf / IsDerivedFrom 繼承鏈判斷

BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const {
    const CRuntimeClass* pClassThis = this;
    while (pClassThis != NULL) {
        if (pClassThis == pBaseClass) return TRUE;
#ifdef _AFXDLL
        pClassThis = (*pClassThis->m_pfnGetBaseClass)();
#else
        pClassThis = pClassThis->m_pBaseClass;
#endif
    }
    return FALSE;
}

靜態 CRuntimeClass 物件在程式啟動前即完成初始化,全程序僅有一份,故指標比較即可確認類型關係。

4.3 RUNTIME_CLASS 巨集

#define RUNTIME_CLASS(class_name) (&class_name::class##class_name)

快速取得任意類別的 CRuntimeClass*。

五、序列化機制與 CArchive 角色

CArchive 充當物件與儲存媒體(CFile/Socket)間的二進位流代理,重載 <</>> 處理基本型別與複雜物件。

  • 寫入(<<):先寫入類別的 CRuntimeClass 資訊,再呼叫物件的 Serialize。
  • 讀取(>>):在模組狀態的 CRuntimeClass 鏈表中尋找對應類型資訊,動態創建物件,再呼叫 Serialize 還原資料。

因此序列化類別必須支援動態類型資訊與動態創建。若直接呼叫 Serialize 而不經 CArchive,則可省略前兩項特性。

六、綜合範例:可序列化的 CPerson 類別

// CPerson.h
class CPerson : public CObject {
    DECLARE_SERIAL(CPerson)
public:
    CPerson() {} // 預設建構子
    CString m_name; WORD m_number;
    virtual void Serialize(CArchive& ar);
};

// CPerson.cpp
IMPLEMENT_SERIAL(CPerson, CObject, 1) // schema = 1
void CPerson::Serialize(CArchive& ar) {
    CObject::Serialize(ar); // 呼叫基類
    if (ar.IsStoring()) ar << m_name << m_number;
    else ar >> m_name >> m_number;
}

// 使用範例
CPerson a; ASSERT(a.IsKindOf(RUNTIME_CLASS(CPerson)));
CRuntimeClass* pClass = RUNTIME_CLASS(CPerson);
CObject* pObj = pClass->CreateObject(); // 動態創建
ASSERT(pObj->IsKindOf(RUNTIME_CLASS(CPerson)));
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved