随笔-19  评论-2  文章-0  trackbacks-0

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

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理