来源:互联网 发布:网络中沙发是什么意思 编辑:程序博客网 时间:2024/05/02 01:00
IMPLEMENT_DYNAMIC什么意思
IMPLEMENT_DYNAMIC是实现“运行时类型识别”宏,与之相对应的是DECLARE_DYNAMIC(声明“运行时类型识别”宏)。也就是说你在.CPP文件中如果看见有IMPLEMENT_DYNAMIC,则在.H文件中必定有DECLARE_DYNAMIC的声明。
DECLARE_DYNAMIC/DEClARE_DYNAMIC是为了确定运行时对象属于哪一个类而定义的宏。
DEClARE_DYNCREATE/IMPLEMENT_DYNCREATE是为了“动态创建"类的实例而定义的宏。new可以用来创建对象,但不是动态的。比如说,你要在程序中实现根据拥护输入的类名来创建类的实例,下面的做法是通不过的:
char szClassName[60];
cin >> szClassName;
CObject* pOb=new szClassName; //通不过
这里就要用到DEClARE_DYNCREATE/IMPLEMENT_DYNCREATE定义的功能了。
建议你去找《深入浅出Mfc》中"六大关键技术之仿真”一章看看,上面关于这东西讲解得很清楚。
 



IMPLEMENT_DYNCREATE

IMPLEMENT_DYNCREATE( class_name, base_class_name )

参数: class_name 类的实际名字(不用引号括起来)。
base_class_name 基类的实际名字(不用引号括起来)。

说明:
与DECLARE_DYNCREATE宏一起使用IMPLEMENT_DYNCREATE宏,使CObject的派生类的对象能够在运行时被动态创建。框架利用这种能力来动态地创建新对象,例如,当它在串行化的过程中从磁盘读取对象的时候。在类的实现文件中加入IMPLEMENT_DYNCREATE宏。更多的信息参见“Visual C++程序员指南”中的“CObject对象”主题。
如果你使用了DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏,就可以使用RUNTIME_CLASS宏以及CObject::IsKindOf成员函数在运行时确定对象所属的类。
如果在类的声明中包含了DECLARE_DYNCREATE,那么就必须在类的实现中包含IMPLEMENT_DYNCREATE。

请参阅:
DECLARE_DYNCREATE, RUNTIME_CLASS, CObject::IsKindOf






MFC常用宏的分类及其作用与用法
 
  1.MFC宏概述以及常用宏及作用

  MFC宏是MFC类库的一个重要组成部分。在MFC应用程序中经常出现。根据用途,大致辞可分为消息映射宏,运行时类型识别宏,调试宏和异常处理宏等。

  MFC中常用宏及其作用如下:

  名称 功能

  RUNTIME_CLASS 获得运行时类的CRuntimeClass结构指针 RUNTIME_CLASS( class_name )

  DECLARE_DYNAMIC 提供基本的运行时类型识别(声明) DECLARE_DYNAMIC( class_name )

  IMPLEMENT_DYNAMIC 提供基本的运行时类型识别(实现) IMPLEMENT_DYNAMIC (class_name, base_class_name )

  DECLARE_DYNCREATE 动态创建(声明) DECLARE_DYNCREATE( class_name )

  IMPLEMENT_DYNCREATE 动态创建(实现) IMPLEMENT_DYNCREATE( class_name,base_class_name )

  DECLARE_SERIAL 对象序列化(声明) DECLARE_SERIAL( class_name )

  IMPLEMENT_SERIAL 对象序列化(实现)IMPLEMENT_SERIAL(class_name,base_class_name,wSchema)

  DECLARE_MESSAGE_MAP 声明消息映射表 DECLARE_MESSAGE_MAP()

  BEGIN_MESSAGE_MAP 开始建立消息映射表 BEGIN_MESSAGE_MAP( theClass, baseClass )

  END_MESSAGE_MAP 结构建立消息映射表 END_MESSAGE_MAP()

  ON_COMMAND 命令消息映射宏

  ON_MESSAGE 自定义消息映射宏

  ON_WM_... MFC预定义消息映射宏

  ON_BN_...,ON_EN_...等 控件通知消息映射宏

  2.部分宏说明:

  a.RUNTIME_CLASS宏使程序能实时创建类的实例。为了让这个宏起作用,定义的类必须从CObject类派生而来,并且在派生类的定义中必须使用宏DECLARE_DYNAMIC,DECLARE_DYNCREATE或DECLARE_SERIAL,在派生类的实现文件中必须使用宏IMPLEMENT_DYNAMIC,IMPLEMENT_DYNCREATE或IMPLEMENT_SERIAL。

  b.DECLARE_DYNAMIC只能使CObject派生类对象具有基本的类型识别功能,可以通过CObject::IsKindOf(ClassName)测试对象与给定类ClassName的关系。

  c.DECLARE_DYNCREATE包括了DECLARE_DYNAMIC的功能。DECLARE_SERIAL包括了DECLARE_DYNAMIC和 DECLARE_DYNCREATE的功能。

  3.MFC调试宏

  a.跟踪宏:TRACE(<表达式>)

  b.断言宏:ASSERT(<表达式>)

  当ASSERT宏的参数不为真时,暂停程序的执行。Examda提示:如果表达式的值为真,则不做任何事情。如果参数为假时,则暂停程序的执行并弹出一个对话框,告诉用户程序暂停运行的行以及所在文件信息。用户可以根据情况选择终止程序运行,调试程序或继续运行。

  c.断言有效宏 ASSERT_VALID ASSERT_VALID(<指针>)

  该宏用于检查指针和对象的用效性。对于一般指针,只检查指针是否为空。对于MFC对象指针,Examda提示:还调用CObject::AssertValid()成员含数判断对象是否合法。

  ASSERT_VALID与ASSERT类似,也是使用对话框提示用户指针或对象是无效的。如果对象有效,则不做任何事情.






探察RUNTIME_CLASS之类的秘密

学mfc学到文档,视图和框架的时候,知道必须在这三个类的派生类的类声明
里加上DECLARE_DYNCREATE,然后在类声明外合适的地方加上IMPLEMENT_DYNCREA
TE,然后文档,视图和框架,还有文档模板就可以协调工作了。查看msdn,发现
类似的宏有这几对:
DECLARE_DYNAMIC 和 IMPLEMENT_DYNAMIC
DECLARE_DYNCREATE 和 IMPLEMENT_DYNCREATE
DECLARE_SERIAL 和 IMPLEMENT_SERIAL

虽然msdn里介绍了他们的作用,但对于它们为什么会起这样的作用心里却没
底,于是翻了翻mfc的源代码,喜欢钻牛角尖的人可以和我一起来钻一钻。

1。
RUNTIME_CLASS宏的定义是这样的:
#define RUNTIME_CLASS(class_name)
((CRuntimeClass*)(&class_name::class##class_name))
其中##的意思是把##两边的符号都进行宏扩展(如果它们是宏的话),然后把扩展
后的内容连接在一起,中间不加空格。例如:RUNTIME_CLASS(CView)将被扩展成

(CRuntimeClass*)(&CView::classCView)
但这个classCView是什么意思?原来,classCView是由DECLARE_DYNAMIC(CView)
引入的一个public属性的CRuntimeClass类型的静态成员变量:
static const AFX_DATA CRuntimeClass classCView;

原来RUNTIME_CLASS的作用就是引用由DECLARE_DYNAMIC宏引入的静态成员变
量。

2。DECLARE_DYNAMIC(class_name)
由于篇幅的原因,宏的具体定义代码就不列出来了,感兴趣的可以去看文件
afx.h。
该宏往类中声明了三个成员:
protected:
static CRuntimeClass* PASCAL _GetBaseClass();
public:
virtual CRuntimeClass* GetRuntimeClass() const;
static const AFX_DATA CRuntimeClass class##class_name;

有两个成员函数,一个静态成员变量class+类名,同RUNTIME_CLASS相似,如
果是DECLARE_DYNAMIC(CView)的话,这个静态成员变量将是classCView。可见这
个成员变量的名称是和DECLARE_DYNAMIC的参数有关的。在下文我们把这个成员变
量统统记做class##class_name。


这个静态成员和两个成员函数在哪里被初始化和具体实现呢?原来是在IMPL
EMENT_DYNAMIC宏里。

3。IMPLEMENT_DYNAMIC(class_name, base_class_name)
查看它的宏定义,如果_AFXDLL被定义了的话,由DECLARE_DYNAMIC引入的成
员的初始化和实现是这样的:

CRuntimeClass* PASCAL class_name::_GetBaseClass()
{
return RUNTIME_CLASS(base_class_name);
}
CRuntimeClass* class_name::GetRuntimeClass() const
{
return RUNTIME_CLASS(class_name);
}
AFX_COMDAT const AFX_DATADEF
CRuntimeClass class_name::class##class_name =
{
#class_name,
sizeof(class class_name),
0xFFFF,
NULL,
NULL,
&class_name::_GetBaseClass,
NULL
};//这是在初始化静态成员变量class##class_name。
//CRuntimeClass结构的各个成员的意义可查看msdn。


4。_DECLARE_DYNAMIC(class_name)
该宏的定义和DECLARE_DYNAMIC(class_name)基本一样。不同之处是静态成员
class##class_name前面没有const修饰符。

5。DECLARE_DYNCREATE(class_name)

该宏也往类中引入了DECLARE_DYNAMIC宏所引入的那三个成员。除此之外,它
还另外引入了一个成员:
static CObject* PASCAL CreateObject();
该宏引入的成员在IMPLEMENT_DYNCREATE里初始化和实现。

6。IMPLEMENT_DYNCREATE(class_name, base_class_name)
该宏自然是初始化和实现由DECLARE_DYNCREATE引入的成员了。
我们看看CreateObject的实现:
CObject* PASCAL class_name::CreateObject()
{
{
return new class_name;
}
呵,这个函数是如此简单,它就是用CObject类里重载的new操作符创建一个
该类类型的对象。

7。_DECLARE_DYNCREATE(class_name)
该宏引入了和DECLARE_DYNCREATE引入的四个成员差不多的成员。唯一的区别
是该宏引入的静态成员class##class_name前面没有const修饰符。

8。DECLARE_SERIAL(class_name)
该宏引入了和_DECLARE_DYNCREATE所引入的一样的四个成员,另外它还多了
这么一句:
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &
pOb);
原来是把重载操作符operator>>的函数当作该类的友元。于是在操作符函数oper
ator>>中就可以访问该类的成员了。

9。IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)
该宏初始化了成员变量:
CRuntimeClass class_name::class##class_name=
{
#class_name,
sizeof(class class_name),
wSchema,
class_name::CreateObject),
RUNTIME_CLASS(base_class_name),
NULL
};//在这里,class##class_name前面是没有const修饰符的。

该宏还实现了下列函数:
CObject* PASCAL class_name::CreateObject()
{
return new class_name;
}

CRuntimeClass* class_name::GetRuntimeClass() const
{
return RUNTIME_CLASS(class_name);
}

CArchive& AFXAPI operator>>
(CArchive& ar, class_name* &pOb)
{
pOb=(class_name*)ar.ReadObject(RUNTIME_CLASS(class_name));
return ar;
}

该宏还声明了一个函数原型:
AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name));






Dynamic Creation(动态创建)
 程序执行期间,根据动态获得一个类名称,要求程序产生一个对象。“类别型录网”为此提供了基础。如果能够把类的大小记录在类别型录中,把建构函数(不是C++的构造函数,而是指即将出现的CRuntimeClass::CreateObject())也记录在类别型录中,当程序在执行期获得一个类名称,它就可以在“类别型录网”中找出对应的元素,然后调用其建构函数,产生出对象。
类别型录网的元素CRuntimeClass于是发生变化:
Struct CruntimeClass
{
 //Attributes
    LRCSTR m_lpnzClassName;
    Int m_nObjectSize;
UINT m_wSchema; //schema number of the loaded class
CObject *(PASCAL * m_pfnCreateObject)();//NULL=>abstract class
CRuntimeClass *m_pBaseClass;
CObject *CreateObject();
Static CRuntimeClass *PASCAL Load();
//CruntimeClass objects linked together in the simple list
static CRuntimeClass *pFirstClass;
CRuntimeClass * m_pNextClass;
};
 
DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE宏
 为了适应CRuntimeClass中新增的成员变量,再新添加两个宏。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)
以CframeWnd为例,程序如下:
class CframeWnd : public CWnd
{
    DECLARE_DTNCREATE(CFrameWnd);
    …
};
IMPLEMENT_DYNCREATE(CframeWnd,CWnd)
展开如下:
class CframeWnd : public CWnd
{
 public:
    static CRuntimeClass ClassCFrameWnd;
    virtual CRuntimeClass *GetRuntimeClass() const;
    static CObject *PASCAL CreateObject();
    …
};
CObject *PASCAL CframeWnd;;CreateObject()
{   return new CframeWnd; }
static char _lpszCFrameWnd[] = “CFrameWnd”;
CRuntimeClass CframeWnd::classCFrameWnd = { _lpszCFrameWnd,sizeof(CFrameWnd),
           0XFFFF,CframeWnd::CreateObject,RUNTIME_CLASS(CWnd),NULL };
static AFX_CLASSINIT _init_CframeWnd(&CframeWnd::classCFrameWnd);
CRuntimeClass * CframeWnd::GetRuntimeClass() const
{ return &CframeWnd::classCFrameWnd;}
从宏的定义可以清楚的看到,拥有动态创建(Dynamic Creation)能力的类库,必然拥有RTTI能力.因为_DYNCREATE宏覆盖了_DYNAMIC宏.
 
范例程序
MFC.H
#define BOOL int
#define TURE 1
#define FALSE 0
#define LPCSTR LPSTR
#define char * LPSTR;
#define UINT int
#define PASCAL _stdcall
#define TRACE1 printf
#include <iostream.h>
#include <stdio.h>
#include <string.h>
class CObject
Struct CruntimeClass
{
 //Attributes
    LRCSTR m_lpnzClassName;
    Int m_nObjectSize;
UINT m_wSchema; //schema number of the loaded class
CObject *(PASCAL * m_pfnCreateObject)();//NULL=>abstract class
CRuntimeClass *m_pBaseClass;
CObject * CreateObject();
Static CRuntimeClass *PASCAL Load();
//CruntimeClass objects linked together in the simple list
static CRuntimeClass *pFirstClass;
CRuntimeClass * m_pNextClass;
}
Struct AFX_CLASSINIT
{ AFX_CLASSINIT(CRuntimeClass *pNewClass);};
#define RUNTIME_CLASS(class_name) \
        (&class_name::class##class_name)
#define DECLARE_DYNAMIC(class_name) \
public: \
static CRuntimeClass class##class_name; \
virtual CRuntimeClass * GetRuntimeClass() const;
#define DECLARE_DYNCREATE(class_name) \
        DECLARE_DYNAMTIC(class_name) \
        Static CObject *PASCAL CreateObject();
#define _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,pfnNew) \
        static char lpsz##classname[] = #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_name::class##class_name;}\
#define IMPLEMENT_DYNAMIC(class_name , base_class_name) \
        _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)
#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)
class CObject
{
 public:
    CObject::CObject() { }
CObject::~CObject() { }
Virtual CRuntimeClass *GetRuntimeClass() const;
BOOL IsKindOf(const CRuntimeClass *pCLass) const;
 Public:
    static CRuntimeClass classCObject;
    virtual void Sayhell() {cout<<”Hello CObject\n”;}
};
class CCmdTarget : public CObject
{
 DECLARE_DYNAMIC(CCmdTarget)
 public:
    CCmdTarget:: CCmdTarget () { }
    CCmdTarget::~CCmdTarget () { }
};
class CwinThread : public CCmdTarget
{
 DECLARE_DYNAMIC(CWinThread)
 public:
    CwinThread:: CwinThread () { }
    CwinThread::~ CwinThread () { }
    Virtual BOOL InitInstance(){
Return TRUE;
}
virtual int Run(){
return 1;
}
};
class CWnd;
class CWinApp : public CwinThread
{
 DECLARE_DYNAMIC(CWinApp)
 public:
    CWinApp * m_pCurrentWinApp;
    CWnd * m_pMainWnd;
 Public:
    CWinApp::CWinApp() { pCurrentWinApp = this ;}
    CWinApp::~CWinApp() { }
    virtual BOOl InitApplication () {
      Return TRUE;
    }
    virtual BOOL InitInstance () {
       return TRUE;
    }
    virtual int Run () {
       return CWinThread::Run();
    }
};
Class CDocument : public CCmdTarget
{
 DECLARE_DYNAMIC(CDocument)
 public:
    Cdocument:: Cdocument() { }
    Cdocument::~ Cdocument() { }
};
Class CWnd : public CCmdTarget
{
 DECLARE_DYNCREATE(CWnd)
 public:
    CWnd:: CWnd () { }
CWnd::~CWnd () { }
Virtual BOOL Create();
BOOL CreateEx();
virtual BOOL PreCreateWindow();
void SayHello() {cout<<”Hello CWnd\n”;}
};
Class CFrameWnd : public CWnd
{
 DECLARE_DYNCREATE(CFrameWnd)
 public:
    CFrameWnd:: CFrameWnd () { }
    CFrameWnd::~CFrameWnd () { }
    BOOL Create ();
    Virtual BOOL PreCreateWindow();
void SayHello() {cout<<”Hello CFrameWnd\n”;}
 
};
Class CView : public CWnd
{
DECLARE_DYNAMIC(CView)
 public:
    CView:: CView () { }
    CView::~ CView () { }
};
//global function
CWinApp *AfxGetApp();
 
 
MFC.CPP
#include “my.h” //本该包含mfc.h,但为了CmyWinapp的定义
extern CmyWinApp theApp ; //external global object
static char szCObject[] = “CObject”;
struct CRuntimeClass CObject::classCObject =
    {szCObject,sizeof( CObject),0XFFFF,NULL,NULL,NULL };
static AFX_CLASSINIT _init_CObject(&CObject::classCObject);
CRuntimeClass *CRuntimeClass::pFirstClass = NULL;
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass * pNewClass)
{
    pNewClass->m_pNextclass=CRuntimeClass ::pFirstClass;
    CRuntimeClass::pFirstClass = pNewClass;
}
CObject *CRuntimeClass::CreateObject()
{
 if(m_pfnCreateObject == NULL )
    {
        TRACE1(“Error:Trying to create object which is not DECLARE_DYNCREATE nor
DECLARE_DYNAMIC:%hs.\n”,m_lpsClassName);
        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)
    {
        if(strcmp(szClassName,pClass->m_lpszClassName)==0)
           return pClass;
}
TRACE1(“Erroe:class not found:%s \n”,szClassName);
Return NULL;
}
CRuntimeClass *CObject::GetRuntimeClass() const
{
    return &CObject::classCObject;
}
 
BOOL CWnd::Create()
{
    return TRUE;
}
BOOL CWnd::CreateEx()
{
    PreCreateWindow();
    return TRUE;
}
BOOL CWnd::PreCreateWindow()
{
    return TRUE;
}
BOOL CframeWnd::Create()
{
    CreateEx();
    return TRUE;
}
BOOL CframeWnd::PreCreateWindow()
{
    return TRUE;
}
INPLEMENT_DYNAMIC(CcmdTarget,CObject)
INPLEMENT_DYNAMIC(CWinThread, CcmdTarget)
INPLEMENT_DYNAMIC(CWinAPP, CWinThread)
INPLEMENT_DYNAMIC(CWnd,CCmdTarget)
INPLEMENT_DYNCREATE(CFrameWnd, CWnd)
INPLEMENT_DYNAMIC(CDocument, CcmdTarget)
INPLEMENT_ DYNCREATE(CView,CWnd)
//global function
CWinApp * AfxApp()
{
 return theApp.m_pCurrentWinApp;
}
 
MY.H
#include <iostream.h>
#include “mfc.h”
class CmyWinApp : public CWinApp
{
 public:
    CmyWinApp:: CmyWinApp() { }
    CmyWinApp::~ CmyWinApp() { }
    virtual BOOL InitInstance();
};
class CMyFrameWnd : public CframeWnd
{
    DECLARE_DYNCREATE(CMyFrameWnd)
 public:
    CMyFrameWnd();
~CMyFrameWnd() {}
void SayHello() {cout<<”Hello CmyFrameWnd\n”;}
};
class CmyDoc : public Cdocument
{
    DECLARE_DYNCREATE(CMyDoc)
 public:
    CmyDoc:: CmyDoc() { }
    CmyDoc::~ CmyDoc() { }
void SayHello() {cout<<”Hello CMyDoc\n”;}
};
class CMyView : public CView
{
    DECLARE_DYNCREATE(CMyView)
 public:
   CMyView:: CMyView () { }
    CMyView::~ CMyView () { }
void SayHello() {cout<<”Hello CMyView\n”;}
};
//global function
coid PrintAllClasses()
 
MFC.CPP
#include “my.h”
CmyWinApp theApp; //global object
BOOL CMyWinAPP::InitInstance()
{
    m_pMainWnd = new CmyFrameWnd;
    return TRUE;
}
CmyFrameWnd::CmyFrameWnd()
{
    Create();
}
IMPLEMENT_DYNCREATE(CmyFrameWnd,CFrameWnd)
IMPLEMENT_DYNCREATE(CMyDoc,CDocument)
IMPLEMENT_DYNCREATE(CMyView,CView)
void PrintAllClasses()
{
    CRuntimeClass *pClass;
    //just walk through the simple list of regidtered classes
    for(pClass = CRuntimeClass::pFirstClass;pClass!=NULL;
pClass = pCLass->m_pNextClass)
    {
        cout<<pClass->m_lpszClassName<<”\n”;
cout<<pClass->m_nObjectSize<<”\n”;
cout<<pClass->m_wSchema<<”\n”;
}
}
//main()
void main()
{
    CwinApp *pApp = AfxGetApp();
    pApp->InitApplication();
    pApp->InitInstance();
pApp->Run();
   CRuntimeClass *pClassRef;
CObject *pObj;
While(1)
{
        if((pClassRef=CRuntimeClass::Load())==NULL)
           break;
pObj = pClassRef->CreateObject();
if(pObj!=NULL)
    pObj->SayHello();
}
}






Visual C++ MFC 中常用宏的含义
http://www.csai.cn 作者:不详 来源:programfan 2006年2月17日 发表评论 进入社区
1、AND_CATCHAND_CATCH

AND_CATCH(exception_class,exception _object_point_name)

说明:
  定义一个代码块,它用于获取废除当前TRY块中的附加异常类型。使用CATCH宏以获得一个异常类型,然后使用AND_CATCH宏获得随后的异常处理代码可以访问异常对象(若合适的话)已得到关于异常的特别原因的更多消息。在AND_CATCH块中调用THROW_LAST宏以便把处理过程移到下个外部异常框架。AND_CATCH可标记CATCH或AND_CATCH块的末尾。
注释:
  AND_CATCH块被定义成为一个C++作用域(由花括号来描述)。若用户在此作用域定义变量,那么记住他们只在此作用域中可以访问。他也用于exception_object_pointer_name变量。

2、ASSERT

ASSERT(booleanExpression)

说明:
  计算变量的值。如果结构的值为0,那么此宏便打印一个诊断消息并且成讯运行失败。如果条件为非0,那么什么也不做。 诊断消息的形式为: assertion failed in file in line 其中name是元文件名,num是源文件中运行失败的中断号。 在Release版中,ASSERT不计算表达式的值也就不中断程序。如果必须计算此表达式的值且不管环境如何那么用VERIFY代替ASSERT。

注释:
  ASSERT只能在Debug版中用

3、ASSERT_VAILD

ASSERT_VAILD(pObject)

说明:
  用于检测关于对象的内部状态的有效性。ASSERT_VALID调用此对象的AssertValid成员函数(把它们作为自己的变量来传递)。在Release版中ASSERT_VALID什么也不做。在DEBUG版中,他检查指针,以不同于NULL的方式进行检查,并调用对象自己的AssertValid成员函数。如果这些检测中有任何一个失败的话,那么他会以与ASSERT相同的方法显示一个警告的消息。

注释:
  此函数只在DEBUG版中有效。

4、BEGIN_MESSAGE_MAP

BEGIN_MESSAGE_MAP(the class,baseclass)

说明:
  使用BEGIN_MESSAGE_MAP开始用户消息映射的定义。在定义用户类函数的工具(.cpp)文件中,以BEGIN_MESSAGE_MAP宏开始消息映射,然后为每个消息处理函数增加宏项,接着以END_MESSAGE_MAP宏完成消息映射。

5、CATCH

CATCH(exception_class,exception_object_pointer_name)

说明:
  使用此用定义一个代码块,此代码用来获取当前TRY块中都一个异常类型。异常处理代码可以访问异常对象,如何合适的话,就会得到关于异常的特殊原因的更多消息。调用THROW_LAST宏以把处理过程一下一个外部异常框架,如果exception-class是类CExceptioon,那么会获取所有异常类型。用户可以使用CObject::IsKindOf成员函数以确定那个特别异常被排除。一种获取异常的最好方式是使用顺序的AND_CATCH语句,每个带一个不同的异常类型。此异常类型的指针由宏定义,用户不必定义。

注释:
  此CATCH块被定义作一个C++范围(由花括号描述)。如用户在此范围定义变量,那么它们只在吃范围内可以访问。他还可以用于异常对象的指针名。

6、DEBUG_NEW

#define new DEBUG_NEW

说明:
  帮助查找内存错误。用户在程序中使用DEBUG_NEW,用户通常使用new运算符来从堆上分配。在Debug模式下(但定义了一个DEBUG符号),DEBUG_NEW为它分配的每个对象记录文件名和行号。然后,在用户使用CMemoryState::DumpAllObjectSince成员函数时,每个以DEBUG_NEW分配的对象分配的地方显示出文件名和行号。 为了使用DEBUG_NEW,应在用户的资源文件中插入以下指令: #define new DEBUG_NEW 一旦用户插入本指令,预处理程序将在使用new的地方插入DEBUG_NEW,而MFC作其余的工作。但用户编译自己的程序的一个发行版时,DEBUG_NEW便进行简单的new操作,而且不产生文件名和行号消息。

7、DECLARE_DYNAMIC

DECLARE_DYNAMIC(class_name)

说明:
  但从CObject派生一个类时,此宏增加关于一个对象类的访问运行时间功能。把DECLARE_DYNAMIC宏加入类的头文件中,然后在全部需要访问词类对象的.CPP文件中都包含此模块。如果像所描述那样使用DELCARE_DYNAMIC和IMPLEMENT_DYNAMIC宏,那么用户便可使用RUNTIME_CLASS宏和CObject::IsKindOf函数以在运行时间决定对象类。如果DECLARE_DYNAMIC包含在类定义中,那么IMPLEMETN_DYNAMIC必须包含在类工具中。

8、DECLARE_DYNCREATE

DECLARE_DYNCREATE(class_name)

说明:
  使用DECLARE_DYNCRETE宏以便允许CObject派生类的对象在运行时刻自动建立。主机使用此功能自动建立新对象,例如,但它在串行化过程中从磁盘读一个对象时,文件及视图和框架窗应该支持动态建立,因为框架需要自动建立它。把DECLARE_DYNCREATE宏加入类的.H文件中,然后在全部需要访问此类对象的.CPP文件中包含这一模式。如果DECLARE_DYNCREATE包含在类定义中,那么IMPLEMENT_DYNCREATE必须包含在类工具中。

9、DECLARE_MESSAGE_MAP

DECLARE_MESSAGE_MAP()

说明:
  用户程序中的每个CCmdTarget派生类必须提供消息映射以处理消息。在类定义的末尾使用DECLARE_MESSAGE_MAP宏。接着,在定义类成员函数的.CPP文件中,使用BEGIN_MESSAGE_MAP宏,每个用户消息处理函数的宏项下面的列表以及END_MESSAGE_MAP宏。

注释:
  如果在DECLARE_MESSAGE_MAP之后定义任何一个成员,那么必须为他们指定一个新存取类型(公共的,私有的,保护的)。

10、DECLARE_SERIAL

DECLARE_SERIAL(class_name)

说明:
  DECLARE_SERIAL为一个可以串行化的CObject派生类产生必要的C++标题代码。串行化是把某个对象的内容从一个文件读出和写入一文件。在.H文件中使用DECLARE_SERIAL宏,接着在需要访问此类对象的全部.CPP文件中包含此文件。如果DECLARE_SERIAL包含在类定义中,那么IMPLEMENT_SERIAL必须包含在类工具中。DECLARE_SERIAL宏包含全部DECLARE_DYNAMIC,IMPLEMENT_DYCREATE的功能。

11、END_CATCH

END_CATCH

说明:
  标识最后的CATCH或AND_CATCH块的末尾。

12、END_MESSAGE_MAP

END_MESSAGE_MAP

说明:
  使用END_MESSAGE_MAP宏结束用户的消息映射定义
  IMPLEMENT_DYNAMIC
  IMPLEMENT_DYNAMIC(class_name,base_class_name)

说明:
  通过运行时在串行结构中为动态CObject派生类访问类名和位置来产生必要的C++代码。在.CPP文件中使用IMPLEMENT_DYNAMIC宏,接着一次链接结果对象代码

13、IMPLEMENT_DYNCREATE

IMPLEMENT_DYNCREATE(class_name,base_class_name)

说明:
  通过DECLARE_DYNCREATE宏来使用IMPLEMENT_DYNCREATE宏,以允许CObject派生类对象在运行时自动建立。主机使用此功能自动建立对象,例如,但它在串行化过程中从磁盘读去一个对象时,他在类工具里加入IMPLEMENT_DYNCREATE宏。若用户使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏,那么接着使用RUNTIME_CLASS宏和CObject::IsKindOf成员函数以在运行时确定对象类。若declare_dyncreate包含在定义中,那么IMPLEMENT_DYNCREATE必须包含在类工具中。

14、IMPLEMENT_SERIAL

IMPLEMENT_SERIAL(class_name,base_class_name,wSchema)

说明:
  通过运行时在串行结构中动态CObject派生类访问类名和位置来建立必要的C++代码。在.CPP文件中使用IMPLEMENT_SERIAL宏,然后一次链接结果对象代码。

15、ON_COMMAND

ON_COMMAND(id,memberFxn)

说明:
  此宏通过ClassWizard或手工插入一个消息映射。它表明那个函数将从一个命令用户接口(例如一个菜单项或toolbar按钮)处理一个命令消息。当一个命令对象通过指定的ID接受到一个Windows WM_COMMAND消息时,ON_COMMAND将调用成员函数memberFxn处理此消息。在用户的消息映射中,对于每个菜单或加速器命令(必须被映射到一个消息处理函数)应该确实有一个ON_COMMAND宏语句。

16、ON_CONTROL

ON_CONTROL(wNotifyCode,id,memberFxn)

说明:
  表明哪个函数将处理一个常规控制表示消息。控制标识消息是那些从一个控制夫发送到母窗口的消息。

17、ON_MESSAGE

ON_MESSAGE(message,memberFxn)

说明:
  指明哪个函数将处理一用户定义消息。用户定义消息通常定义在WM_USER到0x7FF范围内。用户定义消息是那些不是标准Windows WM_MESSAGE消息的任何消息。在用户的消息映射中,每个必须被映射到一个消息处理函数。用户定义消息应该有一个ON_MESSAGE宏语句。

18、ON_REGISTERED_MESSAGE

ON_REGISTERED_MESSAGE(nmessageVarible,memberFxn)

说明:
  Windows的RegisterWindowsMesage函数用于定义一个新窗口消息,此消息保证在整个系统中是唯一的。此宏表明哪个函数处理已注册消息。变量nMessageViable应以NEAR修饰符来定义。

19、ON_UPDATE_COMMAND_UI

ON_UPDATE_COMMAND_UI(id,memberFxn)

说明:
  此宏通常通过ClassWizard被插入一个消息映射,以指明哪个函数将处理一个用户接口个更改命令消息。在用户的消息映射中,每个用户接口更改命令(比讯被映射到一个消息处理函数)应该有一个ON_UPDATE_COMMAND_UI宏语句。

20、ON_VBXEVENT

ON_VBXEVENT(wNotifyCode,memberFxn)

说明:
  此宏通常通过ClassWizard被插入一个消息映射,以指明哪个函数将处理一个来自VBX控制的消息。在用户的消息映射中每个被映射到一消息处理函数的VBX控制消息应该有一个宏语句。

21、RUNTIME_CLASS

RUNTIME_CLASS(class_name)

说明:
  使用此宏从c++类民众获取运行时类结构。RUNTIME_CLASS为由class_name指定的类返回一个指针到CRuntimeClass结构。只有以DECLARE_DYNAMIC,DECLARE_DYNCREATE或DECLARE_SERIAL定义的CObject派生类才返回到一个CRuntimeClass结构的指针。

22、THROW

THROW(exception_object_pointer)

说明:
  派出指定的异常。THROW中断程序的运行,把控制传递给用户程序中的相关的CATCH块。如果用户没有提供CATCH块,那么控制被传递到一个MFC模块,他打印出一个错误并终止运行。

23、THROW_LAST

THROW_LAST()

说明:
  此宏允许用户派出一个局部建立的异常。如果用户试图排除一个刚发现的异常,那么一般此异常将溢出并被删除。使用THROW_LAST,此异常被直接传送到下一个CATCH处理程序。

24、TRACE

TRACE(exp)

说明:
  把一个格式化字符串送到转储设备,例如,文件或调试监视器,而提供与printf相似的功能。同MS_DOS下C程序的printf一样,TRACE宏是一个在程序运行时跟踪变量值的方便形式。在DEBUG环境中,TRACE宏输出到afxDump。在Release版中他不做任何工作。

注释:
  此宏只在MFC的DEBUG版中有效。

25、TRACE0

TRACE0(exp)

说明:
  与TRACE相似,但他把跟踪字符串放在代码段中,而不是DGROUP,因此使用少的DGROUP空间。TRACE0是一组跟踪宏的一个变体,这些宏可用于调试输出。这一组包括TRACE0,TRACE1,TRACE2和TRACE3,这些宏不同在于所取参数的数目不同。TRACE0只取一个格式化字符串并可用于简单文本消息。TRACE1取一格式化字符串加上一个变量——一个将转储的变量。同样,TRACE2,TRACE3分别取2个或3个参数(在格式化字符串之后)。如果用户以便以了应用程序的发行版,那么它只把数据转储到afxDump。

注释:
  此宏只在MFC的DEBUG中有效。


26、TRACE1

TRACE1(exp,param1)

说明:
  参见TRACE0。

27、TRACE2

TRACE2(exp,param1,param2)

说明:
  参见TRACE0。

28、TRACE3

TRACE3(exp,param1,param2,param3)

说明:
  参见TRACE0。

29、TRY

TRY

说明:
  使用此宏建立一TRY块。一个TRY识别一个可排除异常的代码块。这些异常在随后的CATCH和AND_CATCH块处理。传递是允许的:异常可以传递一个外部TRY块,或者忽略它们或者使用THROW_LAST宏。

30、VERIFY

VERIFY(booleanExpression)

说明:
  在MFC的DEBUG版中,VERIFY宏计算它的变量值。 如果结果为0,那么宏打印一个诊断消息并中止程序。如果条件不为0,那么什么工作也不作。 诊断有如下形式: assertion failed in file in line 其中name是源文件的名字,num是在源文件中失败的中止行号。在MFC的Release版中,VERIFY计算表达式值但不打印或中止程序。例如:如果表达式是个函数调用,那么调用成功。







了解CObject和CRuntimeClass  

--------------------------------------------------------------------------------

作者:不祥  来源于:网络  发布时间:2005-10-5

作者:林水怒 shuinu@163.net
转载请保留作者署名

CObject和CRuntimeClass是MFC中两个非常重要的类/结构,绝大部分MFC类都是以CObject做为基类, CRuntimeClass结构同CObject密不可分,了解它们对于深入理解MFC具有重要意义。
一、CRuntimeClass结构
要理解CObject,我们先来看一下CRuntimeClass这个在MFC中至关重要的一个结构。
每个从CObject中派生的类都有有一个CRuntimeClass对象同它关联以完成在运行时得到类实例的信息或者是它的基类。 在afx.h中它的定义如下:
struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName; //类名,一般是指包含CRuntimeClass对象的类的名称
int m_nObjectSize; //包含CRuntimeClass对象的类sizeof的大小,不包括它分配的内存
UINT m_wSchema; // schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class 指向一个建立实例的构造函数

#ifdef _AFXDLL
CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
CRuntimeClass* m_pBaseClass;
#endif

//以上m_pBaseClass的指针(函数)是MFC运行时确定类层次的关键,它一个简单的单向链表

// Operations
CObject* CreateObject(); //这个函数给予CObject 派生类运行时动态建立的能力
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

//这个函数使用 m_pBaseClass或 m_pfnGetBaseClass遍历整个类层次确定是否pBaseClass指向的类是基类,
//使用它可以判断某类是否是从pBaseClass指向的类在派生来。

// Implementation
void Store(CArchive& ar) const;
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

// CRuntimeClass objects linked together in simple list
CRuntimeClass* m_pNextClass; // linked list of registered classes
};



二、CObject类
CObject是MFC类的大多数类的基类,主要是通过它实现:
(1)、运行类信息;(2)、序列化;(3)、对象诊断输出;(4)、同集合类相兼容;
(1)、运行时类信息:
注意:要想使用CRuntimeClass结构得到运行时类信息,你必须在你的类中包括DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC、 DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE或DECLARE_SERIAL/IMPLEMENT_SERIAL。但你的类必须是从CObject派生的才能使用这些宏, 因为通过DECLARE_DYNAMIC将定义一个实现如下的函数:
CRuntimeClass* PASCAL B::_GetBaseClass()
{
return RUNTIME_CLASS(base_name);
}

其中的RUNTIME_CLASS是这样定义的
#define RUNTIME_CLASS( class_name ) \
(CRuntimeClass *)(&class_name::class##class_name);

即得到类中的CRuntimeClass对象指针,显而易见,如果没有基类你用IMPLEMENT_DYNAMIC时将得到一个编译错误。 除非你象CObject一样不用DECLARE_DYNAMIC而定义和实现了这些函数,CObject中的GetBaseClass只是简单的返回NULL。 实际的DECLARE_DYNAMIC在afx.h中声明如下:
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
static const AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \


IMPLEMENT_DYNAMIC在afx.h中定义如下:
#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) \
CRuntimeClass* PASCAL class_name::_GetBaseClass() \
{ return RUNTIME_CLASS(base_class_name); } \
AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
&class_name::_GetBaseClass, NULL }; \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); } \

其中的CRuntimeClass* GetRuntimeClass() const;被定义为虚函数,以完成在类层次上的重载。 这也是MFC利用多态实现运行时动态类信息的方法。
另外两个DECLARE_DYNCREATE和DECLARE_SERIAL类似。只不过它们多定义和实现了一些函数,对于使用DECLARE_DYNCREATE 要注意的是类必须要有一个无参数的缺省构造函数,因为在DECLARE_DYNCREATE中定义了一个CreateObject函数 用以在动态的建立对象,它只是一条简单的return new class_name。
我们先来看一下序列化:
,CObject实现这些功能绝大部分是通过它里面的CRuntimeClass对象classObject实现的,
CObject不支持多重继承,即表示以CObject为基类的类层次中只能有一个CObject基类。

之所以会这样,就是因为CRuntimeClass对象的成员m_pBaseClass的关系。因为它只是一个单链表。
以下是它在afx.h中的定义:
/////////////////////////////////////////////////////////////////////////////
// class CObject is the root of all compliant objects

#ifdef _AFXDLL
class CObject
#else
class AFX_NOVTABLE CObject
#endif
{
public:


// Object model (types, destruction, allocation)

virtual CRuntimeClass* GetRuntimeClass() const;

// 上面的函数的实现只是很简单的return RUNTIME_CLASS(classObject);

virtual ~CObject(); // virtual destructors are necessary

// Diagnostic allocations
void* PASCAL operator new(size_t nSize);
void* PASCAL operator new(size_t, void* p);
void PASCAL operator delete(void* p);
#if _MSC_VER >= 1200
void PASCAL operator delete(void* p, void* pPlace);
#endif

#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
// for file name/line number tracking using DEBUG_NEW
void* PASCAL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
#if _MSC_VER >= 1200
void PASCAL operator delete(void *p, LPCSTR lpszFileName, int nLine);
#endif
#endif

// Disable the copy constructor and assignment by default so you will get
// compiler errors instead of unexpected behaviour if you pass objects
// by value or assign objects.
protected:
CObject();
private:
CObject(const CObject& objectSrc); // no implementation
void operator=(const CObject& objectSrc); // no implementation

// Attributes
public:
BOOL IsSerializable() const; // 对对象进行序列化
BOOL IsKindOf(const CRuntimeClass* pClass) const; //判是否是

// Overridables
virtual void Serialize(CArchive& ar);

#if defined(_DEBUG) || defined(_AFXDLL)
// Diagnostic Support
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

// Implementation
public:
static const AFX_DATA CRuntimeClass classCObject;
#ifdef _AFXDLL
static CRuntimeClass* PASCAL _GetBaseClass();
#endif
};

如果你在你的类的实现和定义中使用可选宏的其中一个,你必须了解从CObject派生的好处。
第一级的宏是DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC,它允许你在运行时处理类名和类层次中的位置,允许你做有意义的诊断Dump。
第二级的宏是DECLARE_SERIAL/IMPLEMENT_SERIAL,它包括第一级宏所有的功能,允许你进行对象的序列化。

要想彻底了解序列化,不得不了解Archive类,我们将在下回详细论述这个类。希望感兴趣的朋友同我联系共同进步。







MFC抽象类之动态实现技术
  前不久,我在北大和哈工大bbs上问了同 一个问题"抽象类能实例化么 not sure",几天后得到大虾的解答,我自己又深入探讨了一些,写下这篇文章以让和我有同样问题的人能从苦恼中解脱出来.

  众所周知,含有纯虚函数或者构造函数被声明为protected的类被称之为抽象类
,初学者在教科书上一定得到以下结论:"切记:抽象类不允许实例化".但在实际中
有例外.
//in XX.h
class OBJ
{

  protected: OBJ();
   //something else
  friend OBJ*Createobj();//用static也行,mfc就用的是static
 
};

//in XX.cpp

OBJ::OBJ()
{cout<<"a obj is being created"<<endl;
}

OBJ*Createobj()
{return new OBJ();}

//in test.cpp
void main()
{
 OBJ*ptr=Createobj();
 
 delete ptr;
 
}

run后结果如下:
a obj is being created
Press any key to continue

看来是实例成功了,当然这个例子离动态创建还有区别,下面让我们看一看MFC是如何来进行动态创建的(不知你是否发现,在MFC的SDI和MDI程序中,都是用了动态创建机制,只是他使用了一些宏和CRuntimeClass这各类帮忙)

侯捷的"深入浅出MFC"中又很详细的关于这方面的讲解,不过他没有提到微软实现这种做法的目的之一是抽象类实例化的实现.

其实现过程大致如下:MFC利用一些宏DECLARE_DYNCREATE,IMPLEMENT_DYNCREATE将一些主要的东西悄悄的插入到将具有动态生成的类中,我们以SDI的CMainFrame的动态生成为例,看一看CMainFrame的声明文件,他的constuctor可是protected的呦,DECLARE_DYNCREATE在MainFrm.h中加了几个重要的函数,其中对动态创建至关重要的是
    static CObject*PASCAL CreateObject();//他可是用来调用CMainFrame
    static const AFX_DATA CRuntimeClass classCMainFrame;                                        的constuctor
   看看他的实现:
    CObject*PASCAL CreateObject()
   { return new CMainFrame;}

但是我们在源代码中好像看不到谁调用了这个函数,事实上微软很秘密的 把他转移给了CRuntimeClass结构
   看看CRuntimeClass结构
struct CRuntimeClass
{
// Attributes
 //somethingelse
 CObject* (PASCAL* m_pfnCreateObject)();
       //.....
// Operations
 CObject* CreateObject();
 
//...........

};
这里我仅列出两个与动态创建有关的成员.
IMPLEMENT_DYNCREATE宏完成了CMainFrame类中CRuntimeClass classCMainFrame的初始化工作,看下面代码
 #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)

m_pfnCreateObject被赋值为CMainFrame::CreateObject,看出点门道了吧^_^.

再看看CObject* CRuntimeClass::CreateObject()的实现吧.

CObject* CRuntimeClass::CreateObject()
{
   .........
  return (*m_pfnCreateObject)();
}

也许你还是很糊涂,但不知道你了不了解SDI程序的启动过程,它启动时默认打开一个单文档文件,其调用顺序如下:
CWinapp::OnFileNew()--->CSingleDocTemplate::OpenDocumentFile()-->...CDocTemplate::CreateNewFrame()
而CDocTemplate::CreateNewFrame()的实现如下:
 CDocTemplate::CreateNewFrame()
{//......
 CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
  //....
}
其中m_pFrameClass是指向CMainFrame成员CRuntimeClass classCMainFrame的指针,
下面从后到前捋清一下思路:
  m_pFrameClass->CreateObject()返回(*m_pfnCreateObject)(),而后者指向的是static CMainFrame::CObject*PASCAL CreateObject();
现在一切都很清晰了.







学习VC MFC开发必须了解的常用宏和指令1、#include指令  
包含指定的文件   

2、#define指令   
预定义,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善、背后一长串”的宏,它本身并不在编译过程中进行,而是在这之前(预处理过程)就已经完成了

3、#typedef指令
常用来定义一个标识符及关键字的别名它是语言编译过程的一部分,但它并不实际分配内存空间。

4、#ifndef   #else   #endif指令  
条件编译。一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。  
条件编译命令最常见的形式为:  
#ifdef 标识符  
   程序段1
#else
   程序段2  
#endif  
它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。

5、#Pragma 指令  
在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。其格式一般为  
#Pragma Para  
其中Para 为参数,下面来看一些常用的参数。
l          message 参数。它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:  
#Pragma message(“消息文本”)  
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。  
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
#ifdef _X86
         #Pragma message(“_X86 macro activated!”)
         #endif   
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了  
l          另一个使用得比较多的pragma参数是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )  
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。  
l          #pragma once  
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。  
l          #pragma hdrstop  
表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所 以使用这个选项排除一些头文件。有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指 定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。  
l          #pragma resource "*.dfm"  
表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。  
l          #pragma warning( disable : 4507 34; once : 4385; error : 164 )
等价于:  
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
         #pragma warning(once:4385) // 4385号警告信息仅报告一次
         #pragma warning(error:164) // 把164号警告信息作为一个错误。
同时这个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1---4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
l          pragma comment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
常用的lib关键字,可以帮我们连入一个库文件。
每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。例如,对循环优化功能:
#pragma loop_opt(on) // 激活
#pragma loop_opt(off) // 终止
有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,如“Parameter xxx is never used in function xxx”,可以这样:
#pragma warn —100 // Turn off the warning message for warning #100
int insert_record(REC *r)
{ /* function body */ }
#pragma warn +100 // Turn the warning message for warning #100 back on
函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。
每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。

6、宏:__LINE__   和__FILE__
     定义源程序文件名和代码行,这对于调试跟踪代码错误行很有帮助。
__TIME__           :编译时间
__DATE__       :编译日期
__TIMESTAMP__ :文件修改时间

7、调试宏:ASSERT()、VERIFY()、TRACE()
这三个宏在Debug环境下特别有效,常用于代码的跟踪调试。它们是否起作用取决于是否定义了预定义了宏 _DEBUG
l          ASSERT
ASSERT(booleanExpression)
说明:
计算变量的值。如果结构的值为0,那么此宏便打印一个诊断消息并且程序运行失败。如果条件为非0,那么什么也不做。诊断消息的形式为: assertion failed in file in line 其中name是元文件名,num是源文件中运行失败的中断号。 在Release 版中,ASSERT不计算表达式的值也就不中断程序。如果必须计算此表达式的值且不管环境如何那么用VERIFY代替ASSERT。
这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。
ASSERT_VAILD
ASSERT_VAILD(pObject)
说明:
用于检测关于对象的内部状态的有效性。ASSERT_VALID调用此对象的AssertValid成员函数(把它们作为自己的变量来传递)。在 Release版中ASSERT_VALID什么也不做。在DEBUG版中,他检查指针,以不同于NULL的方式进行检查,并调用对象自己的 AssertValid成员函数。如果这些检测中有任何一个失败的话,那么他会以与ASSERT相同的方法显示一个警告的消息。
l          VERIFY
VERIFY(booleanExpression)
说明:
在MFC的DEBUG版中,VERIFY宏计算它的变量值。如果结果为0,那么宏打印一个诊断消息并中止程序。如果条件不为0,那么什么工作也不作。诊断 有如下形式: assertion failed in file in line 其中name是源文件的名字,num是在源文件中失败的中止行号。在 MFC的Release版中,VERIFY计算表达式值但不打印或中止程序。例如:如果表达式是个函数调用,那么调用成功。
l          TRACE
TRACE(exp)
说明:
把一个格式化字符串送到转储设备,例如,文件或调试监视器,功能上和printf相似,可以说就是调试环境下printf的一个拷贝。TRACE宏是一个 在程序运行时跟踪变量值的方便形式。在DEBUG环境中,TRACE宏输出到afxDump。在Release版中他不做任何工作。另外还有一组可以带参 数的相似的宏:TRACE0、TRACE1、TRACE2和TRACE3。提供格式如:
TRACE0(exp)
TRACE1(exp,param1)
TRACE2(exp,param1,param2)
TRACE3(exp,param1,param2,param3)
与TRACE相似,但它把跟踪字符串放在代码段中,而不是DGROUP,因此使用少的DGROUP空间。这些宏的用法和printf类似。

8、消息处理宏:DECLARE_MESSAGE_MAP 、BEGIN_MESSAGE_MAP、END_MESSAGE_MAP
DECLARE_MESSAGE_MAP()
说明:
用户程序中的每个CCmdTarget派生类必须提供消息映射以处理消息。在类定义的末尾使用DECLARE_MESSAGE_MAP宏。接着,在定义类 成员函数的.CPP文件中,使用BEGIN_MESSAGE_MAP宏,每个用户消息处理函数的宏项下面的列表以及END_MESSAGE_MAP宏。
注释:
如果在DECLARE_MESSAGE_MAP之后定义任何一个成员,那么必须为他们指定一个新存取类型(公共的,私有的,保护的)。
BEGIN_MESSAGE_MAP(the class,baseclass)
END_MESSAGE_MAP
说明:
使用BEGIN_MESSAGE_MAP开始用户消息映射的定义。在定义用户类函数的工具(.cpp)文件中,以BEGIN_MESSAGE_MAP宏开始消息映射,然后为每个消息处理函数增加宏项,接着以END_MESSAGE_MAP宏完成消息映射。

9、消息映射宏:ON_COMMAND 、ON_CONTROL、ON_MESSAGE、ON_VBXEVENT、ON_Update_COMMAND_UI和ON_REGISTERED_MESSAGE
ON_COMMAND(id,memberFxn)
说明:
此宏通过ClassWizard或手工插入一个消息映射。它表明那个函数将从一个命令用户接口(例如一个菜单项或toolbar按钮)处理一个命令消息。 当一个命令对象通过指定的ID接受到一个Windows WM_COMMAND消息时,ON_COMMAND将调用成员函数memberFxn处理此消 息。在用户的消息映射中,对于每个菜单或加速器命令(必须被映射到一个消息处理函数)应该确实有一个ON_COMMAND宏语句。
ON_CONTROL(wNotifyCode,id,memberFxn)
说明:
表明哪个函数将处理一个常规控制表示消息。控制标识消息是那些从一个控制夫发送到母窗口的消息。
ON_MESSAGE(message,memberFxn)
说明:
指明哪个函数将处理一用户定义消息。用户定义消息通常定义在WM_USER到0x7FF范围内。用户定义消息是那些不是标准 Windows WM_MESSAGE消息的任何消息。在用户的消息映射中,每个必须被映射到一个消息处理函数。用户定义消息应该有一个 ON_MESSAGE宏语句。
ON_Update_COMMAND_UI(id,memberFxn)
说明:
此宏通常通过ClassWizard被插入一个消息映射,以指明哪个函数将处理一个用户接口个更改命令消息。在用户的消息映射中,每个用户接口更改命令(比讯被映射到一个消息处理函数)应该有一个ON_Update_COMMAND_UI宏语句。
ON_VBXEVENT(wNotifyCode,memberFxn)
说明:
此宏通常通过ClassWizard被插入一个消息映射,以指明哪个函数将处理一个来自VBX控制的消息。在用户的消息映射中每个被映射到一消息处理函数的VBX控制消息应该有一个宏语句。
ON_REGISTERED_MESSAGE(nmessageVarible,memberFxn)
说明:
Windows的RegisterWindowsMesage函数用于定义一个新窗口消息,此消息保证在整个系统中是唯一的。此宏表明哪个函数处理已注册消息。变量nMessageViable应以NEAR修饰符来定义。

10、DEBUG_NEW
#define new DEBUG_NEW
说明:
帮助查找内存错误。用户在程序中使用DEBUG_NEW,用户通常使用new运算符来从堆上分配。在Debug模式下(但定义了一个DEBUG符号), DEBUG_NEW为它分配的每个对象记录文件名和行号。然后,在用户使用CMemoryState::DumpAllObjectSince成员函数 时,每个以DEBUG_NEW分配的对象分配的地方显示出文件名和行号。为了使用DEBUG_NEW,应在用户的资源文件中插入以下指令: #define new DEBUG_NEW 一旦用户插入本指令,预处理程序将在使用new的地方插入DEBUG_NEW,而MFC作其余的工作。但 用户编译自己的程序的一个发行版时,DEBUG_NEW便进行简单的new操作,而且不产生文件名和行号消息。

11、异常宏:TRY、CATCH 、THROW、AND_CATCH、THROW_LAST和END_CATCH
TRY
说明:
使用此宏建立一TRY块。一个TRY识别一个可排除异常的代码块。这些异常在随后的CATCH和AND_CATCH块处理。传递是允许的:异常可以传递一个外部TRY块,或者忽略它们或者使用THROW_LAST宏。
CATCH(exception_class,exception_object_pointer_name)
说明:
使用此用定义一个代码块,此代码用来获取当前TRY块中都一个异常类型。异常处理代码可以访问异常对象,如何合适的话,就会得到关于异常的特殊原因的更多 消息。调用THROW_LAST宏以把处理过程一下一个外部异常框架,如果exception-class是类CExceptioon,那么会获取所有异 常类型。用户可以使用CObject::IsKindOf成员函数以确定那个特别异常被排除。一种获取异常的最好方式是使用顺序的AND_CATCH语 句,每个带一个不同的异常类型。此异常类型的指针由宏定义,用户不必定义。
注释:
此CATCH块被定义作一个C++范围(由花括号描述)。如用户在此范围定义变量,那么它们只在吃范围内可以访问。他还可以用于异常对象的指针名。
THROW(exception_object_pointer)
说明:
派出指定的异常。THROW中断程序的运行,把控制传递给用户程序中的相关的CATCH块。如果用户没有提供CATCH块,那么控制被传递到一个MFC模块,他打印出一个错误并终止运行。
AND_CATCH(exception_class,exception _object_point_name)
说明:
定义一个代码块,它用于获取废除当前TRY块中的附加异常类型。使用CATCH宏以获得一个异常类型,然后使用AND_CATCH宏获得随后的异常处理代 码可以访问异常对象(若合适的话)已得到关于异常的特别原因的更多消息。在AND_CATCH块中调用THROW_LAST宏以便把处理过程移到下个外部 异常框架。AND_CATCH可标记CATCH或AND_CATCH块的末尾。
注释:
AND_CATCH块被定义成为一个C++作用域(由花括号来描述)。若用户在此作用域定义变量,那么记住他们只在此作用域中可以访问。他也用于exception_object_pointer_name变量。
THROW_LAST()
说明:
此宏允许用户派出一个局部建立的异常。如果用户试图排除一个刚发现的异常,那么一般此异常将溢出并被删除。使用THROW_LAST,此异常被直接传送到下一个CATCH处理程序。
END_CATCH
说明:
标识最后的CATCH或AND_CATCH块的末尾。

12、DECLARE_DYNAMIC 、IMPLEMENT_DYNAMIC
DECLARE_DYNAMIC(class_name)
说明:
但从CObject派生一个类时,此宏增加关于一个对象类的访问运行时间功能。把DECLARE_DYNAMIC宏加入类的头文件中,然后在全部需要访问 此类对象的.CPP文件中都包含此模块。如果像所描述那样使用DELCARE_DYNAMIC和IMPLEMENT_DYNAMIC宏,那么用户便可使用 RUNTIME_CLASS宏和CObject::IsKindOf函数以在运行时间决定对象类。如果DECLARE_DYNAMIC包含在类定义中,那 么IMPLEMETN_DYNAMIC必须包含在类工具中。  
IMPLEMENT_DYNAMIC(class_name,base_class_name)
说明:
通过运行时在串行结构中为动态CObject派生类访问类名和位置来产生必要的C++代码。在.CPP文件中使用IMPLEMENT_DYNAMIC宏,接着一次链接结果对象代码

13、DECLARE_DYNCreate、IMPLEMENT_DYNCreate
DECLARE_DYNCreate(class_name)
说明:
使用DECLARE_DYNCRETE宏以便允许CObject派生类的对象在运行时刻自动建立。使用此功能自动建立新对象,例如,但它在串行化过程中从 磁盘读一个对象时,文件及视图和框架窗应该支持动态建立,因为框架需要自动建立它。把DECLARE_DYNCreate宏加入类的.H文件中,然后在全 部需要访问此类对象的.CPP文件中包含这一模式。如果DECLARE_DYNCreate包含在类定义中,那么IMPLEMENT_DYNCreate 必须包含在类工具中。
IMPLEMENT_DYNCreate(class_name,base_class_name)
说明:
通过DECLARE_DYNCreate宏来使用IMPLEMENT_DYNCreate宏,以允许CObject派生类对象在运行时自动建立。主机使用 此功能自动建立对象,例如,但它在串行化过程中从磁盘读去一个对象时,他在类工具里加入IMPLEMENT_DYNCreate宏。若用户使用 DECLARE_DYNCreate和IMPLEMENT_DYNCreate宏,那么接着使用RUNTIME_CLASS宏和CObject:: IsKindOf成员函数以在运行时确定对象类。若declare_dyncreate包含在定义中,那么IMPLEMENT_DYNCreate必须包 含在类工具中。

14、DECLARE_SERIAL、IMPLEMENT_SERIAL
DECLARE_SERIAL(class_name)
说明:
DECLARE_SERIAL为一个可以串行化的CObject派生类产生必要的C++标题代码。串行化是把某个对象的内容从一个文件读出和写入一文件。 在.H文件中使用DECLARE_SERIAL宏,接着在需要访问此类对象的全部.CPP文件中包含此文件。如果DECLARE_SERIAL包含在类定 义中,那么IMPLEMENT_SERIAL必须包含在类工具中。DECLARE_SERIAL宏包含全部DECLARE_DYNAMIC, IMPLEMENT_DYCreate的功能。
IMPLEMENT_SERIAL(class_name,base_class_name,wSchema)
说明:
通过运行时在串行结构中动态CObject派生类访问类名和位置来建立必要的C++代码。在.CPP文件中使用IMPLEMENT_SERIAL宏,然后一次链接结果对象代码。

15、RUNTIME_CLASS
RUNTIME_CLASS(class_name)
说明:
使用此宏从c++类名中获取运行时类结构。RUNTIME_CLASS为由class_name指定的类返回一个指针到CRuntimeClass结构。 只有以DECLARE_DYNAMIC、DECLARE_DYNCreate或DECLARE_SERIAL定义的CObject派生类才返回到一个 CRuntimeClass结构的指针。





MFC六大关键技术之仿真(三)

什么是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),一定是从派生类流向基类。