3.框架理解

MFC编写的的WinMain函数则调用了CWinApp::InitInstance函数,所以InitInstance看起来似乎就是MFC程序的入口点。

CWinApp这个类中除了有一般类都有的构造函数,一个重要的成员函数就是InitInstance,我们知道,在WINDOWS环境下面可以运行同一程序的多个实例,函数InitInstance的作用就是在生成的一个新的实例的时候,完成一些初始化的工作。

 

Windows允许在同一时刻运行程序的几份拷贝。在概念上,应用程序的初始化可以被分为两个部分:一次性的应用程序初始化工作,这些在应用程序第一次运行时完成,以及实例的初始化工作,每次运行程序的一个拷贝时都会执行这些操作,包括第一次运行时。框架中WinMain实现调用这个函数。重载InitInstance以初始化在Windows下运行的应用程序的每个新实例。

通常,重载InitInstance以构造主窗口对象并设置CWinThread::m_pMainWnd数据成员,使其指向这个窗口。

 

InitApplicationInitInstance现在成为MFCCWinApp的两个虚函数,前者负责每一个程序只做一次的操作,后者负责每一个例程都得做一次的操作。 每一个程序指的是进程的概念。每一个例程指的是线程的概念。所以InitApplication负责每一个程序只做一次的操作。而InitInstance是每一个线程都具有的函数,只不过很多情况下,你自己开的线程没有重载该函数。

 

其实在基于CWinApp的程序中InitApplicationInitInstance都只执行一次。InitApplicationMFC的较老的入口函数,现在一般不重载InitApplication而直接重载InitInstance  
   

 

不过如果使用CWinThread或者AfxBeginThread创建线程,新的线程会执行新建CWinThread对象的InitInsatnce函数一次。不过这和CWinApp没有关系。

 

 

2.MFC程序初始化和运行时类型识别

构造顺序:

//1.构造函数执行顺序

// 要构造成员:本类的成员【不考虑基类】,基类构造函数【包含基类成员,基类基类构造函数,基类构造函数体】,构造函数体

// 顺序:基类构造函数,本类的成员,构造函数体

析构顺序与构造相反:

析构函数体,本类的成员,基类析构函数

 

MFC构造顺序:

CObject–>CCmdTarget–>CWinThread–>CWinApp–>CMyApp.

Main顺序:

InitApplication【CWinApp类虚函数,派生类自定义App可覆盖】

InitInstance【CWinThread虚函数,CWinApp定义了自己版本,自定义App定义了自己版本,自定义App的InitInstance里面应该New出来自定义主窗口】

Run【CWinThread虚函数,CWinApp定义了自己的版本,自定义App往往也定义自己版本】

 

关于New出自定义主窗口过程:

自定义FrameWnd函数体中,Create【CWnd的虚函数,CFrameWnd也定义自己的版本】,CFrameWnd::Create函数体中,CreateEx【CWnd的非虚函数】,

CWnd::CreateEX中,PreCreateWindow【CWnd的虚函数,CFrameWnd也定义了自己的版本】

 

 

 

3.MFC宏:DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC分析

 

前置结构:

此结构采用链表的头插法建立一个链表

 

 

 

struct CRuntimeClass

{

    LPCSTR m_lpszClassName;

    int    m_nObjectSize;

    UINT   m_wSchema;

    CObject* (PASCAL* m_pfnCreateObject) ();

    CRuntimeClass*m_pBaseClass;

    static  CRuntimeClass* pFristClass;

    CRuntimeClass*m_pNextClass;

}

 

#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)

 

#define _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew)\

    static char _lpsz##class_name[] = #class_name;\

    CRuntimeClass class_name::class##class_name =\

    {\

        _lpsz##class_name, sizeof(class_name), wSchema, pfnNew, RUNTIME_CLASS(base_class_name), NULL\

    };\

    static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name);\

    CRuntimeClass*  class_name::GetRuntimeClass() const\

    {\

        return &class::class##class_name;\

    }\123456789101112131415161718192021222324252627282930

 

其中

#define RUNTIME_CLASS(class_name)\

(&class_name::class##class_name)

表示:只有基类有CRuntimeClass,派生类才能用DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC

利用以下结构,完成采用头插法构建链表的工作。且由于每个类的CRuntimeClass对象和AFX_CLASSINIT对象都是静态的,所以整个应用程序的

CRuntimeClass构成的链表在程序一开始就已经被建立好了。

 

 

 

struct   AFX_CLASSINIT

{

       AFX_CLASSINIT(CRuntimeClass* pNextClass);

}

AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass)

{

       pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;

       CRuntimeClass::pFirstClass = pNewClass;

}123456789

 

整个CRUntimeClass链表尾所在的应该是CObject对象,对此对象特殊处理【特殊处理的原因:CObject没有基类,IMPLEMENT_DYNAMIC不能使用】

 

 

 

class CObject

{

public:

      virtual CRuntimeClass* GetRuntimeClass() const;

      static CRuntimeClass classCObject;

      ...

};

 

static char szCObject[] = "CObject";

struct CRuntimeClass CObject::classCObject =

{

     szCObject, sizeof(CObject), 0xffff, NULL, NULL, NULL

};

static AFX_CLASSINIT  _init_CObject(&CObject::classCObject);

CRuntimeClass* CObject::GetRuntimeClass() const

{

     return &CObject::classCObject;

}

 

CRuntimeClass*  CRuntimeClass::pFirstClass = NULL;1234567891011121314151617181920

 

 

 

4.Frame3

 

程序类结构:

CObject–>CCmdTarget–>CWinThread–>CWinApp–>CMyWinApp

CObject–>CCmdTraget–>CWnd–>CView–>CMyView

CObject–>CCmdTraget–>CWnd–>CFrameWnd–>CMyFrameWnd

CObject–>CCmdTraget–>CDocument–>CMyDoc

且除四个My类外其他各类都应用了DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC

 

 

 

5.IsKindOf

 

在CObject类中增加成员:

 

 

 

public:

     BOOL IsKindOf(const CRuntimeClass* pClass) const;

 

BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const

{

     CRuntimeClass* pClassThis = GetRuntimeClass();

     while(pClassThis != NULL)

     {

          // 每个类对应的CRuntimeClass成员都是静态的且只有一个,故可以通过比较地址来判断是同一类的CRuntimeClass

          if(pClassThis == pClass)

              return TRUE;

          pClassThis = pClassThis->m_pBaseClass;

     }

     return FALSE;

}123456789101112131415

 

作用:可以从发出此调用的类依次往上找参数中的CRuntimeClass。

 

 

 

6.DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE

 

前面的CRuntimeClass增加两个函数:

 

 

 

CObject* CreateObject();

static CRuntimeClass*  PASCAL Load();

 

CObject* CRuntimeClass::CreateObject()

{

    if(m_pfnCreateObject == NULL)

    {

        TRACE1("Error: Trying to create object which is not\

DECLARE_DYNCREATE  or DECLARE_SERIAL: %hs.\n", m_lpszClassName);

        return NULL;

    }

 

    CObject* pObject = NULL;

    pObject = (*m_pfnCreateObject)();

    return pObject;

}

 

CRuntimeClass* PASCAL CRuntimeClass::Load()

{

    char szClassName[64];

    CRuntimeClass* pClass;

 

    cout << "enter a class name ...";

    cin >> szClassName;

 

    for(pClass = pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)

    {

        if(strcmp(szClassName, pClass->m_lpszClassName) == 0)

            return pClass;

    }

 

    TRACE1("Error: Class not found :%s\n", szClassName);

    return NULL;

}

DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE宏

#define DECLARE_DYNCREATE(class_name)\

    DECLARE_DYNAMIC(class_name)\

    static CObject* PASCAL CreateObject();

 

#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)123456789101112131415161718192021222324252627282930313233343536373839404142434445

 

说明:一旦类应用了DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE,这个类就具备了动态创建的能力,给你此类的CRuntimeClass【可通过类名获得】你能new出此类的对象,应用了DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE的类,也具备IsKindOf的能力。且类要提供默认构造函数【不然无法new】

 

Frame6:main中循环,输入一个类名,此类名对应的类在所维护的CRuntimeClass链表中,动态创建和调用其SayHello。若类不再所维护CRuntimeClass链表中,跳出循环,结束。

 

 

 

7.Persistence机制【永久保存】

 

MFC有一套Serialize机制,目的在于把文件名的选择,文件的开关,缓冲区的建立,数据的读写,提取运算符(>>),插入运算符(<<)的重载,对象的动态创建等都包装起来。

数据读写:

CObList:一个链表可放置从CObject派生下来的对象

CDWordArray:一个数组,每个元素都是double word

CStroke,CRectangle,CCircle:自定义派生自CObject的类

 

写文件机制要考虑保存文件各个元素,屏幕滚动,打印输出,按元素出现的顺序记录。每个类对象记录个格式:

类名长度,

类名,

类元素

 

为简化记录:在每次记录对象内容时,先写入一个代码,表示此对象的类是否在文件中记录过了。如新类,乖乖记录其类名称,如是旧类,则以代码表示。【这样可节省文件大小和程序用于解析的时间】

如何控制文件版本,旧版程序读新版文件,新版程序读旧版文件,最好把版本号码记录上去,最好每个类都有自己的版本号码。

以要记录以下链表为例:

CObList m_graphList:

共六个元素:

元素1:直线【CStroke】

元素2:矩形【CRectangle】

元素3:圆形【CCircle】

元素4:直线【CStroke】

元素5:矩形【CRectangle】

元素6:圆形【CCircle】

相关类:

 

 

 

class CMyDoc : public CDocument

{

    CObject m_graphList;

    CSize m_sizeDoc;

    ...

};

class CStroke : public CObject

{

    CDWordArraym_ptArray;

    ...

};

class CRectangle : public CObject

{

    CRect m_rect;

    ...

};

class CCircle : public CObject

{

    CPoint  m_center;

    UINT m_radius;

    ...

};12345678910111213141516171819202122

 

记录的文件格式:

文档大小

元素个数

 

新类标志【如类第一次出现】

schema

类名长度

类名

类元素内容

 

老类标志【如类之前已出现】

类元素内容

 

理想的Document读写:

 

 

 

void CScribDoc::Serialize(CArchive & ar)

{

    if(ar.IsStoring())

        ar << m_sizeDoc;

    else

        ar >> m_sizeDoc;

 

    m_graphList.Serialize(ar);

}

void CObList::Serialize(CArchive &ar)

{

    if(ar.IsStoring())

    {

        ar << (WORD)m_nCount;

        for(CNode* pNode  =  m_pNodeHead;  pNode != NULL; pNode = pNode->pNext)

        ar << pNode->data;

    }

    else

    {

        WORD nNewCount;

        ar >> nNewCount;

        while(nNewCount--)

        {

            CObject* newData;

            ar >> newData;

            AddTail(newData);

        }

    }

}

 

void CStroke::Serialize(CArchive & ar)

{

    m_ptArray.Serialize(ar);

}

void CDWordArray::Serialize(CArchive& ar)

{

    if(ar.IsStoring())

    {

        ar << (WORD)m_nSize;

        for(int i = 0; i < m_nSize; i++)

            ar << m_pData[i];

    }

    else

    {

        WORD nOldSize;

        ar >> nOldSize;

        for(int i = 0; i < m_nOldSize; i++)

            ar >> m_pData[i];

    }

}

 

void CRectangle::Serialize(CArchive& ar)

{

    if(ar.IsStoring())

        ar << m_rect;

    else

        ar >> m_rect;

}

void CCircle::Serialize(CArchive& ar)

{

    if(ar.IsStoring())

    {

        ar << (WORD)m_center.x;

        ar << (WORD)m_center.y;

        ar << (WORD)m_radius;

    }

    else

    {

        ar >> (WORD&)m_center.x;

        ar >> (WORD&)m_center.y;

        ar >> (WORD&) m_radius;

    }

}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273

 

大概结构:

每个类在每个类的Serialize函数里面对类的成员变量用ar >> / << 成员进行成员读写。当类成员是MFC类型或基本类型时,一般可以直接ar >>/<<后续的处理已经被设计好。

这里涉及在Serialize里面什么成员用ar >>/>>,什么成员用  chengyuan.Serialize(ar)问题【一般对简单成员用前面方式,对复合成员【成员可分拆成多个,或自定义类成员等】用后面方式】

 

DECLARE_SERIAL/IMPLEMENT_SERIAL

 

 

 

#define DECLARE_SERIAL(class_name)\

    DECLARE_DYNCREATE(class_name)\

    friend CArchive& AFXAPI operator >> (CArchive& ar, class_name* &pOb);

 

#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)\

    CObject* PASCAL class_name::CreateObject()\

    {\

        return new class_name;\

    }\

    _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, class_name::CreateObject)\

    CArchive& AFXAPI operator >> (CArchive& ar, class_name* &pOb)\

    {\

        pOb = (class_name*)ar.ReadObject(RUNTIME_CLASS(class_name));\

        return ar;\

    }\123456789101112131415

 

为了在每一个对象被处理【读或写】之前,能够处理琐屑的工作。如判断是否第一次出现,记录版本号码,记录文件名等操作,CRuntimeClass需要Load和Store。

 

 

 

struct CRuntimeClass

{

    LPCSTR m_lpszClassName;

    int m_nObjectSize;

    UINT m_wSchema;

    CObject* (PASCAL* m_pfnCreateObject) ();

    CRuntimeClass*m_pBaseClass;

    CObject* CreateObject();

    void Store(CArchive& ar) const;

    static CRuntimeClass*  PASCALLoad(CArchive& ar, UINT* pwSchemaNum);

 

    static CRuntimeClass*pFirstClass;

    CRuntimeClass*m_pNextClass;

};

 

CRuntimeClass* PASCAL CRuntimeClass::Load(CArchive& ar, UINT* pwSchemaNum)

{

    WORD nLen;

    char szClassName[64];

    CRuntimeClass* pClass;

    ar >> (WORD&) (*pwSchemaNum) >> nLen;

 

    if(nLen >= sizeof(szClassName) || ar.Read(szClassName, nLen) != nLen)

        return NULL;

    szClassName[nLen] = '\0';

 

    for(pClass = pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)

    {

        if(lstrcmp(szClassName, pClass->m_lpszClassName) == 0)

            return pClass;

    }

    return NULL;

}

 

void CRuntimeClass::Store(CArchive& ar) const

{

    WORD nLen = (WORD)lstrlenA(m_lpszClassName);// 可视为类的版本

    ar << (WORD)m_wSchema << nLen;

    ar.Write(m_lpszClassName, nLen*sizeof(char));// 类名及其长度

}12345678910111213141516171819202122232425262728293031323334353637383940

 

为了让整个Serialize机制运行起来,在各个需要进行ar >> / << 成员,的成员对应的类中要DECLARE_SERIAL(class_name)/IMPLEMENT_SERIAL(class_name, base_class_name, nSchema)和void Serialize(CArchive& ar);【Serialize是CObject的一个虚函数】

【DECLARE_SERIAL相比于DECLARE_DYNCREATE就是多了一个重载的>>运算符定义】。

 

posted @ 2020-06-27 19:06  janeyjhon1  阅读(191)  评论(0)    收藏  举报