CObject 是 MFC 絕大多數類別的根基類別。它不直接封裝 Windows 物件,而是提供四大橫切關注點機制,讓衍生類「零成本」擁有強大能力:
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
};
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 框架能在編譯期不知具體類別的情況下,依據範本資訊動態建立文檔、視圖、框架視窗。
CArchive(儲存/載入模式)。CArchive::WriteObject/ReadObject 寫入/讀取 CRuntimeClass 資訊。CRuntimeClass::CreateObject 建立物件 → 呼叫 Serialize(CArchive&)。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 物件)。淺拷貝會導致雙重釋放、資源洩漏、狀態不一致。強制衍生類若需複製,必須顯式實作深拷貝建構子/指派運算子,並在其中正確處理資源複製/引用計數。
// 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 資料傳輸等高階功能。