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 阅读(1040)
评论(0) 编辑 收藏 引用 所属分类:
windows