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

CObject 類別深度剖析:MFC 根類別的四大核心機制與實作原理

     編輯:關於C語言

CObject:MFC 類別體系的「萬物之源」

CObject 是 MFC 絕大多數類別的根基類別。它不直接封裝 Windows 物件,而是提供四大橫切關注點機制,讓衍生類「零成本」擁有強大能力:

  1. 執行期類別資訊 (Runtime Class Information)
  2. 動態建立 (Dynamic Creation)
  3. 序列化/持久化 (Serialization)
  4. 診斷支援 (Diagnostics)

類別定義精讀

class CObject
{
public:
    // 執行期類別資訊
    virtual CRuntimeClass* GetRuntimeClass() const;

    // 虛擬解構子(多型刪除必備)
    virtual ~CObject();

    // 運算子 new/delete 重載(DEBUG 版支援檔案/行號追蹤)
    void* PASCAL operator new(size_t nSize);
    void* PASCAL operator new(size_t, void* p); // placement new
    void PASCAL operator delete(void* p);
#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
    void* PASCAL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
#endif

protected:
    // 預設建構子(protected 防止直接實例化)
    CObject();

private:
    // 禁止複製建構與指派(防止淺拷貝句柄/資源)
    CObject(const CObject& objectSrc);           // no implementation
    void operator=(const CObject& objectSrc);    // no implementation

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

機制一:執行期類別資訊 (RCRTI)

CRuntimeClass 結構

struct CRuntimeClass
{
    LPCSTR m_lpszClassName;          // 類別名稱字串
    int m_nObjectSize;               // 物件大小
    UINT m_wSchema;                  // 序列化版本號
    CObject* (PASCAL* m_pfnCreateObject)(); // 動態建立函數指標
    CRuntimeClass* m_pBaseClass;     // 基類 CRuntimeClass 指標
    // ... 靜態鏈表指標,串聯所有衍生類
};

關鍵宏

  • DECLARE_DYNAMIC(class_name):宣告靜態 classCObject 與 GetRuntimeClass()。
  • IMPLEMENT_DYNAMIC(class_name, base_class_name):定義靜態成員,建立繼承鏈。
  • RUNTIME_CLASS(class_name):取得 CRuntimeClass* 編譯期常數。

應用場景


// 1. 型別安全的向下轉型
if (pObj->IsKindOf(RUNTIME_CLASS(CMyDoc))) {
    CMyDoc* pDoc = (CMyDoc*)pObj;
}

// 2. 序列化時自動識別類別
ar << pObj; // 寫入類別名稱 + 版本 + 資料
ar >> pObj; // 讀取類別名稱 → 查找 CRuntimeClass → 動態建立 → 呼叫 Serialize

機制二:動態建立

擴充宏

  • DECLARE_DYNCREATE(class_name):繼承 DECLARE_DYNAMIC,額外宣告 CreateObject() 靜態函數。
  • IMPLEMENT_DYNCREATE(class_name, base_class_name):實作 CreateObject 回傳 new class_name。

應用:文檔/視圖動態建立

// CDocTemplate::CreateNewDocument()
CDocument* CDocTemplate::CreateNewDocument() {
    CRuntimeClass* pClass = m_pDocumentClass; // 例如 RUNTIME_CLASS(CMyDoc)
    return (CDocument*)pClass->CreateObject(); // 動態建立文檔物件
}

這使 MFC 框架能在編譯期不知具體類別的情況下,依據範本資訊動態建立文檔、視圖、框架視窗。

機制三:序列化

核心流程

  1. 物件關聯 CArchive(儲存/載入模式)。
  2. CArchive::WriteObject/ReadObject 寫入/讀取 CRuntimeClass 資訊。
  3. 載入時:CRuntimeClass::CreateObject 建立物件 → 呼叫 Serialize(CArchive&)。
  4. 衍生類覆寫 Serialize,處理自身資料成員。

必要條件

  • 類別使用 IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)(含版本號)。
  • 資料成員為基本型別、CString、CObject* 指標(自動遞迴序列化)、或支援 <</>> 運算子的類別。
  • 預設建構子必須可存取(protected 即可),供動態建立呼叫。

機制四:診斷支援

函數用途典型實作
AssertValid()DEBUG 版驗證物件內部狀態一致性檢查指標非空、索引在界、不變量成立
Dump(CDumpContext&)輸出物件狀態供除錯檢視dc << "m_nCount = " << m_nCount;

MFC 內部大量使用 ASSERT_VALID(pObj)、afxDump << pObj,自訂類別覆寫這兩函數可大幅提升除錯效率。

禁止複製語意的設計智慧

private:
    CObject(const CObject&);           // 未實作
    void operator=(const CObject&);    // 未實作

理由:MFC 物件通常封裝系統資源(句柄、記憶體、GDI 物件)。淺拷貝會導致雙重釋放、資源洩漏、狀態不一致。強制衍生類若需複製,必須顯式實作深拷貝建構子/指派運算子,並在其中正確處理資源複製/引用計數。

自訂類別接入 CObject 體系:最小範本

// MyClass.h
class CMyClass : public CObject
{
    DECLARE_SERIAL(CMyClass)  // 含 DECLARE_DYNCREATE + 序列化支援
public:
    CMyClass();               // protected 或 public
    virtual ~CMyClass();
    virtual void Serialize(CArchive& ar);
    // ... 業務成員
};

// MyClass.cpp
IMPLEMENT_SERIAL(CMyClass, CObject, 1) // 版本 1

CMyClass::CMyClass() { /* 初始化 */ }
CMyClass::~CMyClass() { /* 清理 */ }

void CMyClass::Serialize(CArchive& ar) {
    CObject::Serialize(ar); // 呼叫基類(雖然 CObject 無資料)
    if (ar.IsStoring()) {
        ar << m_strName << m_nValue;
    } else {
        ar >> m_strName >> m_nValue;
    }
}

總結

CObject 雖無資料成員,卻以極小的開銷(一個虛擬函數表指標、靜態 CRuntimeClass)為整個 MFC 體系提供了:型別識別、動態工廠、持久化框架、診斷基礎設施。理解其四大機制與宏實作原理,能讓您在設計自訂類別時「站在巨人肩上」,輕鬆實現文檔/視圖整合、資料庫記錄集映射、剪貼簿/OLE 資料傳輸等高階功能。

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