CObject,CCmdTarget,
CWnd,CFrameWnd,CDocument,CView
CWinApp,CWinThread
构造顺序:
//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也定义了自己的版本】
前置结构:
此结构采用链表的头插法建立一个链表
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;\
}\
其中
#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;
}
整个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;
程序类结构:
CObject–>CCmdTarget–>CWinThread–>CWinApp–>CMyWinApp
CObject–>CCmdTraget–>CWnd–>CView–>CMyView
CObject–>CCmdTraget–>CWnd–>CFrameWnd–>CMyFrameWnd
CObject–>CCmdTraget–>CDocument–>CMyDoc
且除四个My类外其他各类都应用了DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC
在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;
}
作用:可以从发出此调用的类依次往上找参数中的CRuntimeClass。
前面的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)
说明:一旦类应用了DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE,这个类就具备了动态创建的能力,给你此类的CRuntimeClass【可通过类名获得】你能new出此类的对象,应用了DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE的类,也具备IsKindOf的能力。且类要提供默认构造函数【不然无法new】
Frame6:main中循环,输入一个类名,此类名对应的类在所维护的CRuntimeClass链表中,动态创建和调用其SayHello。若类不再所维护CRuntimeClass链表中,跳出循环,结束。
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;
...
};
记录的文件格式:
文档大小
元素个数
新类标志【如类第一次出现】
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;
}
}
大概结构:
每个类在每个类的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;\
}\
为了在每一个对象被处理【读或写】之前,能够处理琐屑的工作。如判断是否第一次出现,记录版本号码,记录文件名等操作,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));// 类名及其长度
}
为了让整个Serialize机制运行起来,在各个需要进行ar >> / << 成员,的成员对应的类中要DECLARE_SERIAL(class_name)/IMPLEMENT_SERIAL(class_name, base_class_name, nSchema)和void Serialize(CArchive& ar);【Serialize是CObject的一个虚函数】
【DECLARE_SERIAL相比于DECLARE_DYNCREATE就是多了一个重载的>>运算符定义】。
【至于这套机制如何建立和数据读取写入什么时候用ar <