2009-9-2
===========================
《深入解析MFC》筆記 4. CObject
===========================
運(yùn)行時(shí)類的信息
CObject的RTCI(run-time class information,運(yùn)行時(shí)類的信息)特性使開發(fā)人員在運(yùn)行時(shí)可以確定關(guān)于類的信息
DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC
DECLARE_DYNCREATE / IMPLEMENT_DYNCREATE
DECLARE_SERIAL / IMPLEMENT_SERIAL
將這些宏加入到CObject的派生類中,就可以調(diào)用IsKindOf()來測試這個(gè)類的類型,IsKindOf有一個(gè)參數(shù),該參數(shù)有另一個(gè)宏RUNTIME_CLASS創(chuàng)建。
--------------------------------------------------------------------------
RTCI如何運(yùn)作 《深入解析MFC》 P114
#define _DECLARE_DYNAMIC(class_name) \
public: \
static CRuntimeClass class##class_name; \ //創(chuàng)建一個(gè)靜態(tài)成員變量
virtual CRuntimeClass* GetRuntimeClass() const; \ //返回正確的運(yùn)行時(shí)類信息
“##”告訴預(yù)處理器將操作符右側(cè)的部分和左側(cè)的部分連接在一起。
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)
#define _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, \
class_name::CreateObject, &_init_##class_name) \
AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); \
CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) \
{ pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); \
return ar; }
"#"字符串化的宏,將右側(cè)的部分轉(zhuǎn)化成用引號(hào)包括的字符串。
1. 初始化了靜態(tài)CRuntimeClass數(shù)據(jù)成員變量 class##class_name
2. 創(chuàng)建了一個(gè)靜態(tài)AFX_CLASSINIT結(jié)構(gòu)。
struct AFX_CLASSINIT{ AFX_CLASSINIT ( CRuntimeClass* pNewClass ); };
3. IMPLEMENT_DYNAMIC宏會(huì)生成這個(gè)覆蓋成員函數(shù)GetRuntimeClass()。
GetRuntimeClass90會(huì)返回靜態(tài)CRuntimeClass成員變量class##class_name的地址
--------------------------------------------------------------------------
CRuntimeClass類
· LPCSTR m_lpszClassName —— 類的名字。在序列化過程中,作為類狀態(tài)的一部分被寫入讀出。
· UINT m_wSchema —— 也是累的一個(gè)狀態(tài)。 給出類的版本信息
· void Store( CArchive& ar) const —— CArchive::WriteClass()調(diào)用這個(gè)函數(shù)來寫出關(guān)于類的CRuntimeClass結(jié)構(gòu)信息
· static CRuntimeClass* Load ( CArchive& ar, UINT* pwSchemaNum); —— CArchive::ReadClass() 調(diào)用Load()來讀入由Store()寫入的CRuntimeClass信息。
· int m_nObjectSize;
· CRuntimeClass* m_pNextClass; ——維護(hù)一個(gè)已創(chuàng)建的對象的簡單鏈表
· m_pBaseClass —— 保存一個(gè)指向基類的CRuntimeClass結(jié)構(gòu)的指針。IsKindOf和IsDerivedFrom()用它來確定對象的“多態(tài)類型".
· m_pfnCreateObject —— 用于動(dòng)態(tài)創(chuàng)建。DECLARE/IMPLEMENT_DYNCREATE 宏會(huì)將這個(gè)成員函數(shù)的指針指向CMyClass::CreateObject()成員函數(shù)。
調(diào)用CRuntimeClass::CreateObject()時(shí),它會(huì)通過m_pfnCreateObject調(diào)用CMyClass::CreateObject()來返回一個(gè)新的CMyClass。
--------------------------------------
#define RUNTIME_CLASS(class_name) (&class_name::class##class_name)
RUNTIME_CLASS宏輝返回靜態(tài)CRuntimeClass成員數(shù)據(jù),DECLARE_DYNAMIC會(huì)將CRuntimeClass成員變量放置在你的類中(類似于GetRuntimeClass())
--------------------------------------------------------------------------
動(dòng)態(tài)創(chuàng)建
在CObject派生類中增加對動(dòng)態(tài)創(chuàng)建的支持,要使用DECLARE_DYNCREATE 和 IMPLEMENT_DYNCREATE 宏,這些宏在DECLARE_DYNAMIC 宏和
IMPLEMENT_DYNAMIC 宏的基礎(chǔ)上創(chuàng)建,不用在類中同時(shí)使用這兩種宏。
加入這些宏后,調(diào)用CRuntimeClass 的成員函數(shù) CreateObject() 創(chuàng)建對象
CRuntimeClass::CreateObject()
首先進(jìn)行檢查,確定用戶是否指定了正確的宏,然后調(diào)用 CRuntimeClass::m_pfnCreateObject 成員變量指向的函數(shù)
--------------------------------------------------------------------------
MFC中的持續(xù)性(serialization) 《深入解析MFC》 P119
持續(xù)性指存儲(chǔ)對象,并在之后的某個(gè)時(shí)刻恢復(fù)對象的能力。
CArchive
· m_nMode —— 制訂了文檔是否在讀和寫。還可以用它在刪除的時(shí)候關(guān)閉刷新
· IsLoading()、IsStoring()、IsByteSwapping() 和 IsBufferEmpty() —— 用來確定CArchive對象的狀態(tài)
· 操作符 —— CArchive為 CObject派生類、Windows數(shù)據(jù)類型和C++類型定義了插入和提取操作符。
· Map成員 —— 進(jìn)行寫操作時(shí),CArchive維護(hù)了一個(gè)映射表,以便能迅速訪問到類似對象的類信息。還利用這個(gè)映射表來確保對一個(gè)特定的類只寫一次
CRuntimeClass信息,然后在序列化流中引用這個(gè)信息。讀操作時(shí),CArchive會(huì)維護(hù)已經(jīng)創(chuàng)建的對象的數(shù)組,并在數(shù)組中存儲(chǔ)那些已經(jīng)讀出的
CRuntimeClass結(jié)構(gòu)信息。這樣,每當(dāng)CArchive查找一個(gè)寫入到序列化流的引用時(shí),都可以查找該數(shù)組。
· 類成員函數(shù) —— WriteClass()、ReadClass() 和 SerializeClass()等成員函數(shù)用來序列化CRuntimeClass結(jié)構(gòu)信息。
CArchive 操作符實(shí)現(xiàn)
_AFX_INLINE CArchive& CArchive::operator<<(WORD w){
if ( m_lpBufCur + sizeof( WORD ) > m_lpBufMax )
Flush();
*(WORD* ) m_lpBufCur = w;
m_lpBufCur += sizeof(WORD);
return *this;
}
_AFX_INLINE CArchive& CArchive::operator>>(WORD& w){
if( m_lpBufCur + sizeof( WORD ) > m_lpBufMax)
FillBuffer( sizeof(WORD) - (UINT) ( m_lpBufMax - m_lpBufCur) );
w = *( WORD* ) m_lpBufCur;
m_lpBufCur += sizeof( WORD);
return *this;
}
用戶選擇文檔要寫入的文件的名字:
CDocument::OnSaveDocument() //以存儲(chǔ)模式創(chuàng)建了一個(gè)CArchive對象,然后將它貼到由對話框(pFile)打開的文件后面。然后,OnSaveDocument() 調(diào)用文 [doccore.cpp] 檔的Serialize() 方法,最后關(guān)閉文檔
CArchive::CArchive() //2-5,檢查文檔是否是通過IsStoring() 方法保存,為寫操作調(diào)用插入符操作
CMyDocument::Serialize() //
CArchive::IsStoring()
operator<<(CArchive&, CObject* )
CArchive::WriteObject() //6-8, 插入操作符調(diào)用CArchive::WriteObject() 函數(shù),
CArchive::WriteClass() //該函數(shù)又調(diào)用WriteClass()
CMyClass::Serialize() //,然后序列化CMyDocument對象
CArchive::IsStoring()
CArchive::operator<<(WORD)
CArchive::operator<<(DWORD)
operator<<(CArchive& , point)
CArchive::Write()
CArchive::Close()
對同一對象讀操作:
CDocument::OnOpenDocument()
[doccore.cpp]
CArchive::CArchive()
CMyDocument::Serialize() //
CArchive::IsStoring() (FALSE)
operator >> (CArchive&, CMyClass* )
CArchive::ReadObject()
CMyClass::Serialize()
CArchive::IsStoring()
CArchive::operator>>(WORD)
CArchive::operator>>(DWORD)
operator>>(CArchive& , point)
CArchive::Read()
CArchive::Close()
若數(shù)據(jù)大于16KB,SetLoadParams() 和SetStoreParams() 可幫助提高序列化性能。
Serializable必要條件 《深入淺出MFC》P394
1、DECLARE_SERIAL 、IMPLEMENT_SERIAL。
2、派生自 CObject,改寫 Serialize虛函數(shù)
3、加上一個(gè)default構(gòu)造函數(shù)
--------------------------------------------------------------------------
CObject對診斷的支持
TRACE宏
類似于printf的輸出。
(調(diào)用了全局函數(shù)::AfxTrace()(DUMPOUT.CPP))《深入解析MFC》P133-136
對象轉(zhuǎn)儲(chǔ)
Dump() 函數(shù),調(diào)用打印類的狀態(tài)。
(OBJCORE.CPP,CObject::Dump() 只是轉(zhuǎn)出了類名(存儲(chǔ)在CRuntimeClass類中)和this指針的地址)
運(yùn)行時(shí)檢查
斷言
ASSERT宏
AFX.H
#define ASSERT(f)\
do\
{\
if(!(f)&AfxAssertFailedLine(THIS_FILE, __LINE__))\
AfxDebugBreak();\
}while(0)\
對象合法性檢查
AssertValid() (CBJCORE.CPP){ASSERT(this!=NULL);}
#define ASSERT_VALID(pOb) (::AfxAssertValidObject(pOb, THIS_FILE, __LINE__)
內(nèi)存診斷
檢測內(nèi)存泄露
使用CMemoryState 類的 Checkpoint()將要檢測的區(qū)域包裝起來,來檢測內(nèi)存泄露的狀況。
然后使用Difference()來比較兩個(gè)斷點(diǎn),查看兩個(gè)調(diào)用Checkpoint()之間之間是否有泄漏。
CMemoryState記錄的每個(gè)已分配的內(nèi)存塊是以下幾個(gè)之一
· freeBlock —— 由于使用 delayFreeMemDF 而導(dǎo)致釋放被延遲的塊
· objectBlock —— 保留的或者已經(jīng)泄漏的 CObject 對象的數(shù)量。
· bitBlock —— 已分配內(nèi)存的非 CObject 對象的個(gè)數(shù)。
· crtBlock —— 已分配的 C 運(yùn)行時(shí)塊的個(gè)數(shù)。
· ignoredBlock —— MFC檢查所忽略的塊的個(gè)數(shù)。
CMemoryState將分配的次數(shù)保存在m_lCounts數(shù)組中,已分配的各種類型的大小存放在m_lSizes成員數(shù)組中。
m_lHighWaterCount 成員函數(shù)存儲(chǔ)了最大分配數(shù),
m_lTotalCount 記錄了分配的總次數(shù)。
m_memState 和 m_pBlockHeader 成員變量都指向了一個(gè)內(nèi)存塊鏈表的頭節(jié)點(diǎn)。
《深入解析MFC》P140-143
檢測最大內(nèi)存使用
通過在afxMemDF全局變量中鄭家按位或(OR)的枚舉值delayFreeMemDF,可以停止使用內(nèi)存釋放函數(shù)。
如 afxMemDF |= delayFreeMemDF; 統(tǒng)計(jì)程序使用了多大內(nèi)存。
檢查內(nèi)存
若想檢測所有的內(nèi)存分配情況和釋放情況,只要與checkAlwaysMemDF的值進(jìn)行或操作。
如 afxMemDF |= checkAlwaysMemDF.
框架就會(huì)在分配和釋放內(nèi)存是檢測內(nèi)存。
內(nèi)存統(tǒng)計(jì)
DumpStatistics()
轉(zhuǎn)儲(chǔ)所有對象
DumpAllObjectsSince(),轉(zhuǎn)儲(chǔ)所有泄露對象的行、地址、大小。
AFX輔助函數(shù)
AfxDoForAllObjects() 和 AfxDoForAllClasses()
AfxDoForAllObjects()
兩個(gè)參數(shù),一個(gè)紙箱一個(gè)函數(shù)的指針,另一個(gè)是void* 指針。
遍歷內(nèi)存對象鏈表,將他們作為CObject指針傳遞給用戶提供的函數(shù)
AfxDoForAllClasses()
只遍歷CRuntimeClass結(jié)構(gòu)
posted on 2010-03-15 23:05
Euan 閱讀(1122)
評論(0) 編輯 收藏 引用 所屬分類:
windows