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