CObject類
CObject 是大多數MFC類的根類或基類。CObject類有很多有用的特性:對運作時類資訊的支援,對動态建立的支援,對串行化的支援,對象診斷輸出,等等。MFC從CObject派生出許多類,具備其中的一個或者多個特性。程式員也可以從CObject類派生出自己的類,利用CObject類的這些特性。
本章将讨論 MFC如何設計CObject類的這些特性。首先,考察CObject類的定義,分析其結構和方法(成員變量和成員函數)對CObject特性的支援。然後,讨論CObject特性及其實作機制。
CObject的結構
以下是 CObject類的定義:
class CObject
{
public:
// 與動态建立相關的函數
virtual CRuntimeClass* GetRuntimeClass() const;
析構函數
virtual ~CObject(); // virtual destructors are necessary
// 與構造函數相關的記憶體配置設定函數,可以用于DEBUG下輸出診斷資訊
void* PASCAL operator new(size_t nSize);
void* PASCAL operator new(size_t, void* p);
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:
// 預設構造函數
CObject();
private:
// 複制構造函數,私有
CObject(const CObject& objectSrc); // no implementation
// 指派構造函數,私有
void operator=(const CObject& objectSrc); // no implementation
// Attributes
// 與運作時類資訊、串行化相關的函數
BOOL IsSerializable() const;
BOOL IsKindOf(const CRuntimeClass* pClass) const;
// Overridables
virtual void Serialize(CArchive& ar);
// 診斷函數
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
// Implementation
// 與動态建立對象相關的函數
static const AFX_DATA CRuntimeClass classCObject;
#ifdef _AFXDLL
static CRuntimeClass* PASCAL _GetBaseClass();
};
由上可以看出, CObject定義了一個CRuntimeClass類型的靜态成員變量:
CRuntimeClass classCObject
還定義了幾組函數:
構造函數析構函數類,
診斷函數,
與運作時類資訊相關的函數,
與串行化相關的函數。
其中,一個靜态函數: _GetBaseClass;五個虛拟函數:析構函數、GetRuntimeClass、Serialize、AssertValid、Dump。這些虛拟函數,在CObject的派生類中應該有更具體的實作。必要的話,派生類實作它們時可能要求先調用基類的實作,例如Serialize和Dump就要求這樣。
靜态成員變量 classCObject和相關函數實作了對CObjet特性的支援。
CObject類的特性
下面,對三種特性分别描述,并說明程式員在派生類中支援這些特性的方法。
對運作時類資訊的支援
該特性用于在運作時确定一個對象是否屬于一特定類(是該類的執行個體),或者從一個特定類派生來的。 CObject提供IsKindOf函數來實作這個功能。
從 CObject派生的類要具有這樣的特性,需要:
定義該類時,在類說明中使用 DECLARE_DYNAMIC(CLASSNMAE)宏;
在類的實作檔案中使用 IMPLEMENT_DYNAMIC(CLASSNAME,BASECLASS)宏。
對動态建立的支援
前面提到了動态建立的概念,就是運作時建立指定類的執行個體。在MFC中大量使用,如前所述架構視窗對象、視對象,還有文檔對象都需要由文檔模闆類(CDocTemplate)對象來動态的建立。
從CObject派生的類要具有動态建立的功能,需要:
定義該類時,在類說明中使用DECLARE_DYNCREATE(CLASSNMAE)宏;
定義一個不帶參數的構造函數(預設構造函數);
在類的實作檔案中使用IMPLEMENT_DYNCREATE(CLASSNAME,BASECLASS)宏;
使用時先通過宏RUNTIME_CLASS得到類的RunTime資訊,然後使用CRuntimeClass的成員函數CreateObject建立一個該類的執行個體。
例如:
CRuntimeClass* pRuntimeClass = RUNTIME_CLASS(CNname)
//CName 必須有一個預設構造函數
CObject* pObject = pRuntimeClass->CreateObject();
// 用IsKindOf檢測是否是CName類的執行個體
Assert( pObject->IsKindOf(RUNTIME_CLASS(CName));
對序列化的支援
“序列化”就是把對象内容存入一個檔案或從一個檔案中讀取對象内容的過程。從 CObject派生的類要具有序列化的功能,需要:
定義該類時,在類說明中使用DECLARE_SERIAL(CLASSNMAE)宏;
在類的實作檔案中使用IMPLEMENT_SERIAL(CLASSNAME,BASECLASS)宏;
覆寫Serialize成員函數。(如果直接調用Serialize函數進行序列化讀寫,可以省略前面三步。)
對運作時類資訊的支援、動态建立的支援、串行化的支援層(不包括直接調用Serailize實作序列化),這三種功能的層次依次升高。如果對後面的功能支援,必定對前面的功能支援。支援動态建立的話,必定支援運作時類資訊;支援序列化,必定支援前面的兩個功能,因為它們的聲明和實作都是後者包含前者。
綜合示例:
定義一個支援串行化的類CPerson:
class CPerson : public CObject
DECLARE_SERIAL( CPerson )
CPerson(){}{};
CString m_name;
WORD m_number;
void Serialize( CArchive& archive );
// rest of class declaration
} ;
實作該類的成員函數 Serialize,覆寫CObject的該函數:
void CPerson::Serialize( CArchive& archive )
// 先調用基類函數的實作
CObject::Serialize( archive );
// now do the stuff for our specific class
if( archive.IsStoring() )
archive << m_name << m_number;
else
archive >> m_name >> m_number;
}
使用運作時類資訊:
CPerson a;
ASSERT( a.IsKindOf( RUNTIME_CLASS( CPerson ) ) );
ASSERT( a.IsKindOf( RUNTIME_CLASS( CObject ) ) );
動态建立:
CRuntimeClass* pRuntimeClass = RUNTIME_CLASS(CPerson)
//Cperson 有一個預設構造函數
Assert( pObject->IsKindOf(RUNTIME_CLASS(CPerson));