什么是application framework?內部運作機制是什么?
MFC六大關鍵技術是什么?
1MFC程序的初始化過程
2RTTI 動態類型標識
3Dynamic Creation 動態生成
4Persistence 永久保留
5Message Mapping 信息映射
6Message Routing 信息傳遞
怎樣自制RTTI?
我們作為類庫的設計者要在類構造起來的時候,記錄必要的信息,以建立型錄。型錄中的類的信息,最好以鏈表方式連接起來。一般“類別型錄”是一個結構,其中至少需要類名字,鏈表的Next指針,以及鏈表的First指針。First屬于整體變量,一份就好,所以用static修飾。
為了將每一個類都能擁有成員變量集合,并且最好有一定的命名規則,然后經某種手段將整個類庫構造好之后,“類別型錄”(就是各個CRuntimeClass對象)則能呈現為:
什么是DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC 宏?作用就是完成RTTI的“類別型錄”。
為了將一個類對象塞到類之中,并定義一個可以捕捉到該對象地址的函數,定義一個宏為:
#define DECLARE_DYNAMIC(class_name)
public:
static CRuntimeClass class##class_name;
virtual CRuntimeClass* GetRuntimeClass()const;
比如我使用了DECLARE_DYNAMIC(CView)
編譯器預處理器為我做出的代碼是:
public:
static CRuntimeClass classCView;
virtual CRuntimeClass * GetRuntimeClass()const;
也就是定義類時,將類放入DECLARE_DYNAMIC宏就是把要放的對象放到了里邊。具體連接工作是由IMPLEMENT_DYNAMIC宏來實現:
#define IMPLEMENT_DYNAMIC(class_name,base_class_name)
_IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)
這里不做擴展,總之,IMPLEMENT_DYNAMIC內容不僅制定初值,它使用了一個struct AFX_CLASSINIT {AFX_CLASSINTI(CRuntimeClass * pNewClass);};
(c++的struct和class都有構造函數):
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass*pNewClass)
{ pNewClass->m_NextClass = CRuntimeClass::pFirstClass;
CRuntimeClass::pFirstClass = pNewClass;
}
就是上邊的這個構造函數完成了連接工作。
一般使用的形式是:
class CView:public CWnd
{
DECLARE_DYNAMIC(CView)
...
};
// in implementation file
IMPLEMENT_DYNAMIC(CView CWnd)
這兩個宏就完成了構造數據鏈表的工作。
怎樣生成mfc層次結構的類別型錄?
.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是什么?
它是類型識別,就說在建立了“類別型錄”網后,在某個類中存在這個函數,就是看這個指針是不是存在某個類的下支中。
比如:
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(動態生成)?
將類的大小記錄在類別型錄中,把構造函數也記錄在類別型錄中,當程序在運行時期獲得一個類名字,它就可以在“類別型錄網”中找到對應的元素,然后根據類的大小,調用構造函數,產生出該對象。
這里使用的是DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE宏
使用和完成類別型錄的方式差不多:
class CFrameWnd:public CWnd
{
DECLARE_DYNCREATE(CFrameWnd)
...
};(就是在頭文件中)
// in implement (就是在cpp文件中實現)
IMPLEMENT_DYNCREATE(CFrameWnd,CWnd)
怎樣自制Persistence(永久保存)機制?
寫到文件中去。
把數據寫到文件中。方法是:在Document/View結構中,數據都放在一份document里頭,我們只要把其中的成員變量陸續寫進文件中即可。如果成員變量是個對象,就需要先記載類的名字,然后才是對象中的數據。
什么是Serialize機制?
就是把文件名的選擇、文件的訪問、緩沖區的建立、數據的讀寫、運算符(>>)和運算符(<<)的重載、對象的動態生成都包裝起來。它的數據讀寫和對象的動代生成是關鍵,動態生成已經具有,這里就重點討論數據的讀寫操作。
serialize機制就是考慮到每次記錄對象內容的時候,先寫入一個代碼,表示此對象類是否曾在文件中記錄過了。如果是新類,就記錄類的名字,如果是就類,就用代碼表示。還有就是可以控制版本號的問題。有一個專門的serialization函數,用于負責這些任務。
每一個可寫到文件或可從文件中讀出的類,都應該有它自己的serailize函數。負責它自己數據讀寫操作,并且應該改寫“<<”“>>”,把數據導流導archive中。
怎樣完成serialize?
使用DECLARE_SERIAL/IMPLEMENT_SERIAL宏。這個宏的功能是將“<<”和“>>”兩個運算符重載,還可以將serialize函數放到類定義中。類能夠進行文件讀寫,其前提是擁有動態生成的能力。
#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;}
一個對象處理之前,判斷是否第一次出現、記錄版本號、記錄文件名怎樣實現?
用CRuntimeClass中的兩個函數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
};
為了讓整個serialization機制運行起來,必須做定義為:
.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(消息映射)?
當我們的類庫成立,如果其中與信息有關的類(就是“信息標的類”mfc中就是CCmdTarget)都是一條鏈式地繼承,我們應該為每一個“信息表的類”準備一個信息映射表,比且將基類與派生類的信息映射表連接起來。然后,當窗口函數做信息的比較時,我們就可以想辦法引導它沿著這條路走過去。
定義消息映射的數據結構:
struct AFX_MSGMAP
{
AFX_MSGMAP * pBaseMessagMap;
AFX_MSGMAP_ENTRY *lpEntries;
};
其中的AFX_MSGMAP_ENTRY又是另一個數據結構:
struct AFX_MSGMAP_ENTRY
{
UINT nMessage;
UINT nCode;
UINT nID;
UINT nLastID;
UINT nSig;
AFX_PMSG pfn;
}
其中的AFX_PMSG定義為函數指針:
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對消息傳遞的規定是什么?(message routing)
如果是一般的windows信息(WM_xxx),一定是從派生類流向基類。
posted on 2009-07-18 19:24
Bluesea 閱讀(2152)
評論(0) 編輯 收藏 引用 所屬分類:
MFC