文章目錄
- 1.CRuntimeClass的定義
- 2.宏DECLARE_DYNCEATE
- 3.宏IMPLEMENT_DYNCREATE
- 4.建立對象
在用到MFC的文檔視圖構架你可能有個非常迷惑的地方.就是很多類不知道在哪裡就被莫名其妙的執行個體化了.
以單文檔視圖為例.代碼中你能看到的的執行個體化的地方就只有兩個一個是CWinApp的一個全域變數的執行個體化,另一個就是
CSingleDocTemplate 執行個體化.它的建構函式如下
CSingleDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass);
然後接下來的事就你迷惑了,先是繼承自SingleDocTemplate的類執行個體化了繼承自CFrameWnd的類和繼承自CDocument的類,然後呢繼承自CFrameWnd的類又執行個體化了繼承自CView的類.
你可能會想到背後早有寫好的代碼去執行個體化這些類了,但是最令你疑惑的是我自己定義繼承自啥CView,CDocument等類的類時名字是自己隨便取的啊,之前寫好的代碼竟然能有未蔔Crowdsourced Security Testing的能力知道這類名? 不然不知道一個類的類名怎麼去執行個體化它啊?
MFC自然沒這麼神奇的能力.所以毫無疑問的一點是你定義的新類類名資訊肯定會傳到某個地方,然後在那裡執行個體化該類.背後的機制咋樣的呢?
這就是所謂的動態產生(dynamic creation)機制了啊.該機制有用到前面講的RTTI,在RTTI的基礎上再擴充一些功能.
動態建立的一個簡單例子1.CRuntimeClass的定義
首先CRuntimeClass定義中除了RTTI資訊,添加
struct CRuntimeClass{
CObject* ( *m_pfnCreateObject)(); //函數指標,指向的函數中會通過new執行個體化一個對象
CObject* CreateObject()
//其他資訊省略掉了
}
其中函數CreateObject定義如下
CObject* CRuntimeClass::CreateObject(){
CObject* pObect = NULL;
pObject =(*m_pfnCreateObject)();
}
2.宏DECLARE_DYNCEATE
假如定義一個繼承自CDocument的類CMyDoc.
我們瞧下CSingleDocTemplate* pTmp = new(.... RUNTIME_CLASS(CMyDoc) ....) //這裡忽略其他參數.
CMyDoc是怎麼被建立的呢? 先看CMyDoc定義中的宏
////////CMyDoc標頭檔//////
DECLARE_DYNCEATE(CMyDoc)
///////////////////////////////////////////////
相當於:
static CObject* CreateObject();
static CRuntimeClass classCMyDoc;
宏定義:
#define DECLARE_DYNCREATE(class_name) \
DECLARE_DYNAMIC(class_name) \
static CObject* CreateObject();
3.宏IMPLEMENT_DYNCREATE
///////////CMyDoc cpp檔案/////////////////
IMPLEMENT_DYNCREATE(CMyDoc, CDocument)
/////////////////////////////////////////////////
上面的宏除了前面的RTTI資訊外,還有個
CObject* CMyDoc::CreateObject(){
return new CMyDoc;
}
CMyDoc::classCMyDoc.m_pfnCreateObject = CMyDoc::CreateObject;
宏定義:
#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
CObject* class_name::CreateObject() \
{ return new class_name; } \
//IMPLEMENT_RUNTIMECLASS(......)//為討論方便這裡省略掉了
4.建立對象
有了上面兩個宏,然後CSingleDocTemplate的建構函式中有個參數RUNTIME_CLASS(CMyDoc),我們知道
#define RUNTIME_CLASS(class_name) (class_name::GetThisClass())
的以傳的參數實際上是一個指向CMyView中的static CRuntimeClass classCMyDoc的指標.
於是可以這樣調用函數建立對象
CObject* pDoc = RUNTIME_CLASS(CMyDoc)->CreateObject();//實際上就是調用
CObject* CMyDoc::CreateObject(){
return new CMyDoc;
}
總結:
你可能看到上面一堆亂七八糟的東東,就想著真他媽扯蛋啊,搞到最後不就是只需要一個類的名字,然後用該名字來執行個體化一個類嘛.有必要搞得這麼麻煩嘛
該功能說起來簡單,但實現起來還真沒其他簡單方法啊,除非C++文法做些更改,編譯器添加些額外支援.我們知道函數中可以隨便傳個啥參數.
但執行個體化一個類比如CView* view = new CView; //一個類的名字是不能做為參數的啊.
比如void CreateObject(string className){
className* view = new className;
}
這樣的函數絕對無法通過編譯的.但我們上面講到的貌似是這樣實現的.因為那是宏,只是簡單的文字替換,宏的替換那一步還沒有輪到編譯器去幹活.宏替換完了才是編譯器上場.
有了宏可以把類名當"參數"一樣用可以說實現了一大半功能了.實際上你如果只是通過前面的兩個宏整出一個靜態函數static CObject* CreateObject();完全就可以了.但這樣顯然實現的不夠優雅嘛,並且既然有個CRuntimeClass了就乾脆把動態建立對象的函數帶到該結構體中豈不更好啊(很多地方都用到CRuntimeClass,動態類型識別,動態建立,序列化).