在用到MFC的文档视图构架你可能有个非常迷惑的地方.就是很多类不知道在哪里就被莫名其妙的实例化了.
以单文档视图为例.代码中你能看到的的实例化的地方就只有两个一个是CWinApp的一个全局变量的实例化,另一个就是
CSingleDocTemplate 实例化.它的构造函数如下
CSingleDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass);
然后接下来的事就你迷惑了,先是继承自SingleDocTemplate的类实例化了继承自CFrameWnd的类和继承自CDocument的类,然后呢继承自CFrameWnd的类又实例化了继承自CView的类.
你可能会想到背后早有写好的代码去实例化这些类了,但是最令你疑惑的是我自己定义继承自啥CView,CDocument等类的类时名字是自己随便取的啊,之前写好的代码竟然能有未卜先知的能力知道这类名? 不然不知道一个类的类名怎么去实例化它啊?
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,动态类型识别,动态创建,序列化).