SDI和MDI分析、RTTI、序列化

3.单文档总结

(1)MFC的winMain:
    在vs的安装路径下面的文件winmain.cpp 中的唯一个函数
    int AFXAPI AfxWinMain(…)
    {
    …
    if (pApp != NULL && !pApp->InitApplication())
    if (!pThread->InitInstance())
    nReturnCode = pThread->Run();//从run里面就进入了消息循环
    …
    }
    通过这一段代码可以非常清楚的知道,MFC程序是怎样通过比较大的三个步骤进入消息循环,然后再进入MFC的消息机制的。
    注意的是pApp和pThread实际上都是同一个指针。
    InitApplication()和InitInstance()都是虚函数,不过在单文档中只重载了InitInstance()函数,所以我们可以忽略掉InitApplication()函数。
   
1)InitApplication()
在这个函数中我们只需要注意下面的一句,表示文档是动态创建的。(动态创建和RUNTIME_CLASS有关)
CDocManager::bStaticInit = FALSE;
2)分析InitInstance()
首先我们需要知道,管理文档的是CSingleDocTemplate(继承于CDocManager)。
下面贴出在InitInstance()中最主要的一段代码

CSingleDocTemplate* pDocTemplate;
 pDocTemplate = new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CsingleDocDoc),
        RUNTIME_CLASS(CMainFrame),       // main SDI frame window
        RUNTIME_CLASS(CsingleDocView));
    if (!pDocTemplate)
        return FALSE;
    AddDocTemplate(pDocTemplate);


    // Parse command line for standard shell commands, DDE, file open
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line.  Will return FALSE if
    // app was launched with /RegServer, /Register, /Unregserver or /Unregister.
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;
       
在分析这段代码之前,我们已经知道了消息循环存在于pThread->Run()中。但是我们的窗体创建和窗口注册又在哪里呢?我们的窗体创建和注册都在ProcessShellCommand中。
RUNTIME_CLASS是一个宏,作用是得到类的CRuntimeClass类型的对象的指针:

#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

补充说明:#的作用是转换成字符串,##的作用就是连接
CRuntimeClass是一个结构体,里面存有这个类的名字,和用于动态创建的函数:CreateObject。
至于CRuntimeClass是怎样引入到一个类里面和怎样填完的,我们就必须知道动态创建:
DECLARE_DYNCREATE给一个类引入了三个非常重要的东西来支持动态创建:

#define _DECLARE_DYNCREATE(class_name) \
    _DECLARE_DYNAMIC(class_name) \
    static CObject* PASCAL CreateObject();

#define DECLARE_DYNAMIC(class_name) \
protected: \
    static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
    static const CRuntimeClass class##class_name; \
    static CRuntimeClass* PASCAL GetThisClass(); \
    virtual CRuntimeClass* GetRuntimeClass() const; \
   
在 IMPLEMENT_DYNCREATE 给出了这些实现:
#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
    CObject* PASCAL class_name::CreateObject() \
        { return new class_name; } \
    IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
        class_name::CreateObject, NULL)
       
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \
    CRuntimeClass* PASCAL class_name::_GetBaseClass() \
        { return RUNTIME_CLASS(base_class_name); } \
    AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
        #class_name, sizeof(class class_name), wSchema, pfnNew, \
            &class_name::_GetBaseClass, NULL, class_init }; \
    CRuntimeClass* PASCAL class_name::GetThisClass() \
        { return _RUNTIME_CLASS(class_name); } \
    CRuntimeClass* class_name::GetRuntimeClass() const \
        { return _RUNTIME_CLASS(class_name); }
       
所以 new CSingleDocTemplate(…)的作用就很简单了:
CDocTemplate 里面有成员变量分别存放 doc,frame,view的 runtime_class类型的指针。其中IDR_MAINFRAME 是用来表示资源的,也用一个变量存放着。 
存放着干什么呢? 存放着给ProcessShellCommand 里面用来调用它们的 CreateObject() 来创建各自的对象。

AddDocTemplate(pDocTemplate) ;  的作用:
在 CWinApp 里面有一个变量m_pDocManager, 用它来存放 pDocTemplate。
m_pDocManager->AddDocTemplate(pTemplate);

至此,如果你记住了 m_pDocManager, pDocTemplate, DOC, Frame, view 它们三者之间的关系, 并且知道doc,frame,view是在ProcessShellCommand里面创建的话,那么你就基本上搞清楚 singledoc 的结构了。
Doc,frame的创建比较容易找到,提醒一下view的创建在 CFrameWnd::CreateView


注意的是:我们可以重载一些函数来达到我们的目的。

 

MDI总结:

MDI中下面的对象需要关注:theApp,DocManager, docTemplate, mainFrame,childFrame, view, doc

一个app可以有多个docTemplate.一个mainFrame可以有多个childFrame;一个childFrame可以有多个view;但是docview是多对一的关系。

 

(1).theApp有一个CDocManager类的对象成员,用来管理docTemplate

CDocManager 类则拥有一个指针链表 CPtrList m_templateList来维护这些文档模版。这些文档模版都是在 CAApp:: InitInstance()中通过 AddDocTemplate(pDocTemplate)

CDocTemplate拥有 3个成员变量,分别保存着 Document View Frame CRuntimeClass指针,另外持有成员变量 m_nIDResource,用来指定这个 Document显示时候采用的菜单资源。这 4份数据都在 CAApp:: InitInstance()中 CDocTemplate的构造函数中指定。在 Document中拥有一个回指 CDocTemplate的指针( m_pDocTemplate)。

DocTemplate还有一个成员变量 m_docList 保存着所有doc的指针。

 

具体的可以看下面的代码:

  

 

(2) 一个文档可以有多个视图,由 Document中成员变量 CPtrList m_ViewList维护。

CView 中拥有一个成员变量 CDocument* m_pDocument指向该视图相关的 Document

 

(3).我们在程序中找不到 childFramelist和所有viewlist,这些list只是在系统内部维护,看不到源代码。

但是我们可以确定的是:在任何时刻,程序中只有一个活动的文档、框架和视图,即当前的文档、框架、视图。

需要注意:childFrame有自己的菜单,和mainframe的菜单不一样,看以参考word文档。


 

 

 

1.       RTTI(Runtime Type Information)

在CRuntimeClass这个结构体里面存储有类名、类的基类的CRuntimeClass对象,CreateObject等信息,所以就能组成一个链表一样的数据结构。 我们利用这些信息就能实现对象的动态创建和对象的类型识别了。

 

CRuntimeClass对象链表的头:

我们可以看CObject里面有一个定义,它就是链表的头。

链表的建立:

在IMPLEMENT_DYNCREATE或者其他IMPLEMENT宏里面(不同版本的vs可能有差别)有下面的定义:

实际上链表的物理建立是由 对象AFX_CLASSINIT 这个对象实现的,我们看看它的构造函数就明白了:

需要注意的是,即使没有这个物理的链表,我们也有一个逻辑的链表,因为我们可以得到基类的CRuntimeClass对象,但是这条链表的功能相对于AFX_CLASSINIT就弱了许多。

 

进行对象动态识别的方法:

CObject提供了两个函数 GetRuntimeClass 和IsKindOf 两个成员函数来支持对象动态识别。

GetRuntimeClass 根据对象的类返回 CRuntimeClass的指针。

IsKindOf 用于测试对象和类的关系。

 

5.序列化
    序列化的作用:
    通过序列化可以把类的成员变量的值(也可以说是类的当前状态)保存到一个文件里,文件通常是二进制或者xml文件。然后再通过反序列化可以把这些值从文件中读取出来给一个类,来初始化一个类。
    序列化也叫串行化,反序列化也叫反串行化。
   
    MFC中序列化依赖的对象: CArchive类和CFile类。 CFile类的作用是以二进制方式存取文件。
    序列化必须做的四件事:定义的类必须继承于CObject; 必须引入两个宏DECLARE_SERIAL() 和IMPLEMENT_SERIAL(); 必须实现自己的 Serialize() 虚函数;最后一点,必须有默认构造函数,因为会动态创建(CreateObject函数里面的new是调用默认构造函数的)。
    Ex:
    //cpp 文件
    IMPLEMENT_SERIAL(CArwen, CObject,1)//1是版本号,可以随便指定一个数值
    void CArwen::Serialize(CArchive &ar)
    {
        if(ar.IsStoring()){//序列化,保存信息。CArchive::store 与此对应
            ar << age;
            ar >> name;
        }else{//反序列化,读取信息
        {
            ar >> age;
            ar >> name;
        }
    }

    .测试程序
   
        CString filePath = _T("D:\\...");//这里是文件的路径
       
        void TestStore()
        {
            CArwen arwen;
            arwen.age = 24;
            arwen.name = _T("weiwen");
            CFile fi;
            fi.open(filePath, CFile::modeCreate|CFile::modeWrite);
            CArchive ar(&fi,CArchive::store);
            awen.Serialize(ar);//序列化
        }
       
        void testRead()
        {
            CArwen arwen;
            CFile fi;
            fi.open(filePath, CFile::Read);
            CArchive ar(&fi,CArchive::load);
            awen.Serialize(ar);//反序列化
            ar.Close();
            fi.Close();
           
        }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值