什么是application framework?內(nèi)部運(yùn)作機(jī)制是什么?
MFC六大關(guān)鍵技術(shù)是什么?
1MFC程序的初始化過程
2RTTI 動(dòng)態(tài)類型標(biāo)識(shí)
3Dynamic Creation 動(dòng)態(tài)生成
4Persistence 永久保留
5Message Mapping 信息映射
6Message Routing 信息傳遞
怎樣自制RTTI?
我們作為類庫的設(shè)計(jì)者要在類構(gòu)造起來的時(shí)候,記錄必要的信息,以建立型錄。型錄中的類的信息,最好以鏈表方式連接起來。一般“類別型錄”是一個(gè)結(jié)構(gòu),其中至少需要類名字,鏈表的Next指針,以及鏈表的First指針。First屬于整體變量,一份就好,所以用static修飾。
為了將每一個(gè)類都能擁有成員變量集合,并且最好有一定的命名規(guī)則,然后經(jīng)某種手段將整個(gè)類庫構(gòu)造好之后,“類別型錄”(就是各個(gè)CRuntimeClass對(duì)象)則能呈現(xiàn)為:
什么是DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC 宏?作用就是完成RTTI的“類別型錄”。
為了將一個(gè)類對(duì)象塞到類之中,并定義一個(gè)可以捕捉到該對(duì)象地址的函數(shù),定義一個(gè)宏為:
#define DECLARE_DYNAMIC(class_name)
public:
static CRuntimeClass class##class_name;
virtual CRuntimeClass* GetRuntimeClass()const;
比如我使用了DECLARE_DYNAMIC(CView)
編譯器預(yù)處理器為我做出的代碼是:
public:
static CRuntimeClass classCView;
virtual CRuntimeClass * GetRuntimeClass()const;
也就是定義類時(shí),將類放入DECLARE_DYNAMIC宏就是把要放的對(duì)象放到了里邊。具體連接工作是由IMPLEMENT_DYNAMIC宏來實(shí)現(xiàn):
#define IMPLEMENT_DYNAMIC(class_name,base_class_name)
_IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)
這里不做擴(kuò)展,總之,IMPLEMENT_DYNAMIC內(nèi)容不僅制定初值,它使用了一個(gè)struct AFX_CLASSINIT {AFX_CLASSINTI(CRuntimeClass * pNewClass);};
(c++的struct和class都有構(gòu)造函數(shù)):
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass*pNewClass)
{ pNewClass->m_NextClass = CRuntimeClass::pFirstClass;
CRuntimeClass::pFirstClass = pNewClass;
}
就是上邊的這個(gè)構(gòu)造函數(shù)完成了連接工作。
一般使用的形式是:
class CView:public CWnd
{
DECLARE_DYNAMIC(CView)
...
};
// in implementation file
IMPLEMENT_DYNAMIC(CView CWnd)
這兩個(gè)宏就完成了構(gòu)造數(shù)據(jù)鏈表的工作。
怎樣生成mfc層次結(jié)構(gòu)的類別型錄?
.h文件中
class CObject{...};
class CCmdTarget:public CObject
{
DECLARE_DYNCMIC (CCmdTarget)
...
};
class CWinThread:public CCmdTarget
{
DECLARE_DYNAMIC (CWinThread)
...
};
class CWinApp:public CWinThread
{
DECLARE_DYNAMIC (CWinApp)
...
};
class CDocument:public CCmdTarget
{
DECLARE_DYNAMIC (CDocument)
...
};
class CWnd:public CCmdTarget
{
DECLARE_DYNAMIC (CWnd)
...
};
class CFrameWnd:public CWnd
{
DECLARE_DYNAMIC (CFrameWnd)
...
};
class CView:public CWnd
{
DECLARE_DYNAMIC (CView)
...
};
class CMyWinApp:public CWinApp
{...};
class CMyFrameWnd:public CFrameWnd
{...};
class CMyDoc:public CDocument
{...};
class CMyView:public CView
{...};
.cpp文件中
IMPLEMENT_DYNAMIC(CCmdTarget,CObject)
IMPLEMENT_DYNAMIC(CWinThread,CCmdTarget)
IMPLEMENT_DYNAMIC(CWinApp,CWinThread)
IMPLEMENT_DYNAMIC(CWnd,CCmdTarget)
IMPLEMENT_DYNAMIC(CView,CWnd)
IMPLEMENT_DYNAMIC(CFrameWnd,CWnd)
IMPLEMENT_DYNAMIC(CDocument,CCmdTarget)
IsKindOf是什么?
它是類型識(shí)別,就說在建立了“類別型錄”網(wǎng)后,在某個(gè)類中存在這個(gè)函數(shù),就是看這個(gè)指針是不是存在某個(gè)類的下支中。
比如:
CMyDoc * pMyDoc = new CMyDoc;
CMyView * pMyView = new CMyView;
cout<<pMyDoc->IsKindOf(RUNTIME_CLASS(CMyDoc));//TURE
cout<<pMyDoc->IsKindOf(RUNTIME_CLASS(CDocument));//TURE
cout<<pMyDoc->IsKindOf(RUNTIME_CLASS(CCmdTarget));//TURE
cout<<pMyDoc->IsKindOf(RUNTIME_CLASS(CObject));//TURE
cout<<pMyDoc->IsKindOf(RUNTIME_CLASS(CWinApp));//FALSE
怎樣 自制Dynamic Create(動(dòng)態(tài)生成)?
將類的大小記錄在類別型錄中,把構(gòu)造函數(shù)也記錄在類別型錄中,當(dāng)程序在運(yùn)行時(shí)期獲得一個(gè)類名字,它就可以在“類別型錄網(wǎng)”中找到對(duì)應(yīng)的元素,然后根據(jù)類的大小,調(diào)用構(gòu)造函數(shù),產(chǎn)生出該對(duì)象。
這里使用的是DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE宏
使用和完成類別型錄的方式差不多:
class CFrameWnd:public CWnd
{
DECLARE_DYNCREATE(CFrameWnd)
...
};(就是在頭文件中)
// in implement (就是在cpp文件中實(shí)現(xiàn))
IMPLEMENT_DYNCREATE(CFrameWnd,CWnd)
怎樣自制Persistence(永久保存)機(jī)制?
寫到文件中去。
把數(shù)據(jù)寫到文件中。方法是:在Document/View結(jié)構(gòu)中,數(shù)據(jù)都放在一份document里頭,我們只要把其中的成員變量陸續(xù)寫進(jìn)文件中即可。如果成員變量是個(gè)對(duì)象,就需要先記載類的名字,然后才是對(duì)象中的數(shù)據(jù)。
什么是Serialize機(jī)制?
就是把文件名的選擇、文件的訪問、緩沖區(qū)的建立、數(shù)據(jù)的讀寫、運(yùn)算符(>>)和運(yùn)算符(<<)的重載、對(duì)象的動(dòng)態(tài)生成都包裝起來。它的數(shù)據(jù)讀寫和對(duì)象的動(dòng)代生成是關(guān)鍵,動(dòng)態(tài)生成已經(jīng)具有,這里就重點(diǎn)討論數(shù)據(jù)的讀寫操作。
serialize機(jī)制就是考慮到每次記錄對(duì)象內(nèi)容的時(shí)候,先寫入一個(gè)代碼,表示此對(duì)象類是否曾在文件中記錄過了。如果是新類,就記錄類的名字,如果是就類,就用代碼表示。還有就是可以控制版本號(hào)的問題。有一個(gè)專門的serialization函數(shù),用于負(fù)責(zé)這些任務(wù)。
每一個(gè)可寫到文件或可從文件中讀出的類,都應(yīng)該有它自己的serailize函數(shù)。負(fù)責(zé)它自己數(shù)據(jù)讀寫操作,并且應(yīng)該改寫“<<”“>>”,把數(shù)據(jù)導(dǎo)流導(dǎo)archive中。
怎樣完成serialize?
使用DECLARE_SERIAL/IMPLEMENT_SERIAL宏。這個(gè)宏的功能是將“<<”和“>>”兩個(gè)運(yùn)算符重載,還可以將serialize函數(shù)放到類定義中。類能夠進(jìn)行文件讀寫,其前提是擁有動(dòng)態(tài)生成的能力。
#define DECLARE_SERIAL(class_name)
DECLARE_DYNCREATE(class_name)
friend CArchive& AFXAPI operator >>(CArchive& ar,class_name *&pOb)
#define IMPLEMENT_SERIAL(class_name,base_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;}
一個(gè)對(duì)象處理之前,判斷是否第一次出現(xiàn)、記錄版本號(hào)、記錄文件名怎樣實(shí)現(xiàn)?
用CRuntimeClass中的兩個(gè)函數(shù)Load和Store。
struct CRuntimeClass
{
//attributes
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema;//schema number of the loaded class
CObject *(PASCAL * m_pfnCreateObject)();
CRuntimeClass * m_pBaseClass;
CObject *CreateObject();
void Store (CArchive&ar)const;
static CRuntimeClass * PASCAL Load(CArchive &ar,UINT *pwSchemaNum);
//CRuntimeClass objects linked together in simple list
static CRuntimeClass * pFirstClass;//start of class list
CRuntimeClass * m_pNextClass;//linked list of registered classes
};
為了讓整個(gè)serialization機(jī)制運(yùn)行起來,必須做定義為:
.h文件中必須有
class CScribDoc:public CDocument
{
DECLARE_DYNCREATE(CScribDoc)
...
};
class CStroke:public CObject
{
DECLARE_SERIAL(CStroke)
public:
void Serialize(CArchive&);
...
};
class CRectangle :public CObject
{
DECLARE_SERIAL(CRectangle)
public
void Serialize(CArchive&)
};
class CCircle:public CObjcet
{
DECLARE_SERIAL(CCircle)
public:
void Seiralize(CArchive&);
...
};
.cpp文件中必須有
IMPLEMENT_DYNCREATE(CScribDoc,CDocument)
IMPLEMENT_SERIAL(CStroke,CObjcet,2)
IMPLEMENT_SERIAL(CRectangle,CObjcet,1)
IMPLEMENT_SERIAL(CCircle,CObjcet,1)
怎樣自制Message Mapping(消息映射)?
當(dāng)我們的類庫成立,如果其中與信息有關(guān)的類(就是“信息標(biāo)的類”mfc中就是CCmdTarget)都是一條鏈?zhǔn)降乩^承,我們應(yīng)該為每一個(gè)“信息表的類”準(zhǔn)備一個(gè)信息映射表,比且將基類與派生類的信息映射表連接起來。然后,當(dāng)窗口函數(shù)做信息的比較時(shí),我們就可以想辦法引導(dǎo)它沿著這條路走過去。
定義消息映射的數(shù)據(jù)結(jié)構(gòu):
struct AFX_MSGMAP
{
AFX_MSGMAP * pBaseMessagMap;
AFX_MSGMAP_ENTRY *lpEntries;
};
其中的AFX_MSGMAP_ENTRY又是另一個(gè)數(shù)據(jù)結(jié)構(gòu):
struct AFX_MSGMAP_ENTRY
{
UINT nMessage;
UINT nCode;
UINT nID;
UINT nLastID;
UINT nSig;
AFX_PMSG pfn;
}
其中的AFX_PMSG定義為函數(shù)指針:
typedef void (CCmdTarget::*AFX_PMSG)(void);
之后定義的宏就是
#define DECLARE_MESSAGE_MAP();
static AFX_MSGMAP_ENTRY_ messageEntries[];
static AFX_MSGMAP messageMap;
virtual AFX_MSGMAP *GetMessageMap()const;
MFC對(duì)消息傳遞的規(guī)定是什么?(message routing)
如果是一般的windows信息(WM_xxx),一定是從派生類流向基類。
posted on 2009-07-18 19:24
Bluesea 閱讀(2152)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
MFC