今天上来有N小时了,居然发现没一个适用偶回答的问题.看来水平不行,须得多加努力.
郁闷之余,发小贴一张,资高手牛人探讨.如版猪大人有幸路过,请为置顶,在下感激不尽!检查CView的源代码ViewCore.cpp时,在文件尾发现这样几行:
// IMPLEMENT_DYNAMIC for CView is in wincore.cpp for .OBJ granularity reasons
IMPLEMENT_DYNAMIC(CSplitterWnd, CWnd)   // for swap tuning
IMPLEMENT_DYNAMIC(CCtrlView, CView)
在这里,IMPLEMENT_DYNAMIC和文件的内容并不匹配.事实上,该文件中仅包含了视图类的主要实现部分和CCtrlView的部分实现,但绝对没有CSplitterWnd的实现.想像中本该存在于此的IMPLEMENT_DYNCREATE(CFrameWnd, CWnd)却没有出现,经查找发现它位于WinFrm.cpp文件尾(该文件主要实现框架类CFrameWnd):
// in this file for IsKindOf library granularity (IsKindOf references these)
IMPLEMENT_DYNCREATE(CFrameWnd, CWnd)
IMPLEMENT_DYNAMIC(CView, CWnd)
现在的问题是:MFC这样将IMPLEMENT_DYNAMIC放在看似随意的文件中的目的是什么呢?

解决方案 »

  1.   

    《深入浅出MFC》第二版中讲得非常详细了。
      

  2.   

    "现在的问题是:MFC这样将IMPLEMENT_DYNAMIC放在看似随意的文件中的目的是什么呢?"
    ----------------------------------------------------------------------------
    为什么说“看似随意的文件”呢?什么语句放在什么地方必定是有道理的。你要Split View,总不能让一个View自个儿完成切分自已并实现、管理切分出来的新类吧,要知道,切分完之后,分成的几个VIEW都应是平级的,谁也不比谁 的Level高,那么如何调度呢,谁来调度呢,肯定要由比他们Level高一点的来做,它就是框架窗体CFrameWnd.
      

  3.   

    可以支持动态创建
    类信息,
    和序列化支持
    典型的用法如:
    m_pViewModule[Programer_Edit] = CreateView(RUNTIME_CLASS(CProgramer_EditView));
    CWnd* CMainFrame::CreateView(CRuntimeClass * pViewClass)
    {
    ASSERT(pViewClass);

    CWnd* pWnd;
    TRY
    {
    pWnd = (CWnd*)pViewClass->CreateObject();
    if (pWnd == NULL)
    AfxThrowMemoryException();
    }
    CATCH_ALL(e)
    {
    TRACE0("Out of memory creating a splitter pane.\n");
    return NULL;
    }
    END_CATCH_ALL

    ASSERT_KINDOF(CWnd, pWnd);
    ASSERT(pWnd->m_hWnd == NULL);       // not yet created

    DWORD dwStyle = AFX_WS_DEFAULT_VIEW;
    if (afxData.bWin4)
    dwStyle &= ~WS_BORDER;
    static i=0;

    // Create with the right size (wrong position)
    CRect rect(CPoint(0,0), CPoint(0,0));
    if (!pWnd->Create(NULL, NULL, dwStyle,
    rect, &m_wndSplitter2,  1700+i, NULL))
    {
    TRACE0("Warning: couldn't create client pane for splitter.\n");
    // pWnd will be cleaned up by PostNcDestroy
    return NULL;
    }
    i++;

    pWnd->SendMessage(WM_INITIALUPDATE);
    return pWnd;

    }
      

  4.   

    Use the IMPLEMENT_DYNCREATE macro with the DECLARE_DYNCREATE macro to enable objects of CObject-derived classes to be created dynamically at run time. The framework uses this ability to create new objects dynamically, for example, when it reads an object from disk during serialization. Add the IMPLEMENT_DYNCREATE macro in the class implementation file. For more information, seeCObject Class Topics in Visual C++ Programmer’s Guide.这是msdn上的原话,他说明,位置也不是随意的。
    一个是在头文件,一个是在cpp里。
    这个宏只是一个包装宏,是其他几个的组合
      

  5.   

    楼主有个星星啊,好羡慕為了因應 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 為例,㆘列程式碼:
    // in header file
    class CFrameWnd : public CWnd
    {
    DECLARE_DYNCREATE(CFrameWnd)
    ...
    };
    // in implementation file
    IMPLEMENT_DYNCREATE(CFrameWnd, CWnd)
    就被展開如㆘(注意,編譯器選項 /P 可得前置處理結果):
    // in header file
    class CFrameWnd : public CWnd
    {
    public:
    static CRuntimeClass classCFrameWnd;
    virtual CRuntimeClass* GetRuntimeClass() const;
    static CObject* PASCAL CreateObject();
    ...
    };
    // in implementation file
    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; }
    圖示如㆘:
    “ C O b je c t ”
    C O b je c t : :c la s s C O b je c t
    m _ p B a s e C la s s
    m _ p N e x tC la s s
    “C C m d T a rg e t”
    C C m d T a rg e t: : c la s s C C m d T a rg e t
    m _ p B a s e C la s s
    m _ p N e x tC la s s
    “C W in T h re a d ”
    C W in T h re a d : :c la s s C W in T h r e a d
    m _ p B a s e C la s s
    m _ p N e x tC la s s
    “C W n d ”
    C W n d : : c la s s C W n d
    m _ p B a s e C la s s
    m _ p N e x tC la s s
    “C W in A p p ”
    C W in A p p ::c la s s C W in A p p
    m _ p B a s e C la s s
    m _ p N e x tC la s s
    N U L L
    C R u n t im e C la s s ::p F ir s tC la s s
    N U L L
    “C F ra m e W n d ”
    C F ram eW n d ::c la s s C F ram eW n d
    m _ p B a s e C la s s
    m _ p N e x tC la s s
    “ C D o c u m e n t ”
    C D o c u m e n t : :c la s s C D o c u m e n t
    m _ p B a s e C la s s
    m _ p N e x tC la s s
    “ C V ie w ”
    C V ie w : :c la s s C V ie w
    m _ p B a s e C la s s
    m _ p N e x tC la s s
    ( s t a t i c 變數)
    「物件生成器」CreateObject 函式很簡單,只要說 new 就好。
    從巨集的定義我們很清楚可以看出,擁有動態生成(Dynamic Creation)能力的類別庫,
    必然亦擁有執行時期型態識別(RTTI)能力,因為 _DYNCREATE 巨集涵蓋了 _DYNAMIC
    巨集。
    注意:以㆘範例直接跳到 Frame6。本書第㆒版有㆒個 Frame5 程式,用以模擬 MFC 2.5
    對動態生成的作法。往事已矣,讀者曾經來函表示沒有必要提過去的東西,徒增腦力負
    荷。我想也是,況且 MFC 4.x 的作法更好更容易瞭解,所以我把 Frame5 拿掉了,但仍
    保留著序號。
      

  6.   

    範例程式Frame6 在 .h 檔㆗有這些類別宣告:
    class CObject
    {
    ...
    };
    class CCmdTarget : public CObject
    {
    DECLARE_DYNAMIC(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_DYNCREATE(CWnd)
    ...
    };
    class CFrameWnd : public CWnd
    {
    DECLARE_DYNCREATE(CFrameWnd)
    ...
    };
    class CView : public CWnd
    {
    DECLARE_DYNAMIC(CView)
    ...
    };
    class CMyWinApp : public CWinApp
    {
    ...
    };
    class CMyFrameWnd : public CFrameWnd
    {
    DECLARE_DYNCREATE(CMyFrameWnd)
    ...
    };
    class CMyDoc : public CDocument
    {
    DECLARE_DYNCREATE(CMyDoc)
    ...
    };
    class CMyView : public CView
    {
    DECLARE_DYNCREATE(CMyView)
    ...
    };
    在 .cpp 檔㆗又有這些動作:
    IMPLEMENT_DYNAMIC(CCmdTarget, CObject)
    IMPLEMENT_DYNAMIC(CWinThread, CCmdTarget)
    IMPLEMENT_DYNAMIC(CWinApp, CWinThread)
    IMPLEMENT_DYNCREATE(CWnd, CCmdTarget)
    IMPLEMENT_DYNCREATE(CFrameWnd, CWnd)
    IMPLEMENT_DYNAMIC(CDocument, CCmdTarget)
    IMPLEMENT_DYNAMIC(CView, CWnd)
    IMPLEMENT_DYNCREATE(CMyFrameWnd, CFrameWnd)
    IMPLEMENT_DYNCREATE(CMyDoc, CDocument)
    IMPLEMENT_DYNCREATE(CMyView, CView)
    於是組織出圖3-2 這樣㆒個大網。
    現在,我們開始模擬動態生成。首先在 main 函式㆗加㆖這㆒段碼:
    void main()
    {
    ...
    //Test Dynamic Creation
    CRuntimeClass* pClassRef;
    CObject* pOb;
    while(1)
    {
    if ((pClassRef = CRuntimeClass::Load()) == NULL)
    break;
    pOb = pClassRef->CreateObject();
    if (pOb != NULL)
    pOb->SayHello();
    }
    }
    並設計 CRuntimeClass::CreateObject 和 CRuntimeClass::Load 如㆘:
    // in implementation file
    CObject* CRuntimeClass::CreateObject()
    {
    if (m_pfnCreateObject == NULL)
    {
    TRACE1("Error: Trying to create object which is not "
    "DECLARE_DYNCREATE \nor DECLARE_SERIAL: %hs.\n",
    m_lpszClassName);
    return NULL;
    }
    CObject* pObject = NULL;
    pObject = (*m_pfnCreateObject)();
    return pObject;
    }
    CRuntimeClass* PASCAL CRuntimeClass::Load()
    {
    char szClassName[64];
    CRuntimeClass* pClass;
    // JJHOU : instead of Load from file, we Load from cin.
    cout << "enter a class name... ";
    cin >> szClassName;
    for (pClass = pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)
    {
    if (strcmp(szClassName, pClass->m_lpszClassName) == 0)
    return pClass;
    }
    TRACE1("Error: Class not found: %s \n", szClassName);
    return NULL; // not found
    }
    然後,為了驗證這樣的動態生成機制的確有效(也就是物件的確被產生了),我讓許多
    個類別的建構式都輸出&#12690;段文字,而且在取得物件指標後,真的去呼叫該物件的&#12690;個成
    員函式 SayHello。我把 SayHello 設計為虛擬函式,所以根據不同的物件型態,會呼叫
    到不同的 SayHello 函式,出現不同的輸出字串。
    請注意,main 函式&#12695;的 while 迴路必須等到 CRuntimeClass::Load 傳回 NULL 才會停
    止,而 CRuntimeClass::Load 是在它從整個「類別型錄網」&#12695;找不到它要找的那個類別
    名稱時,才傳回 NULL。這些都是我為了模擬與示範,所採取的權宜設計。
    Frame6 的命令列編譯聯結動作是(環境變數必須先設定好,請參考第4章的「安裝與設
    定」&#12690;節):
    cl my.cpp mfc.cpp <Enter>
    &#12696;面是Frame6 的執行結果。粗體表示我(程式執行者)在螢幕&#12694;輸入的類別名稱:
    enter a class name... CObject
    Error: Trying to create object which is not DECLARE_DYNCREATE
    or DECLARE_SERIAL: CObject.
    enter a class name... CView
    Error: Trying to create object which is not DECLARE_DYNCREATE
    or DECLARE_SERIAL: CView.
    enter a class name... CMyView
    CWnd Constructor
    CMyView Constructor
    Hello CMyView
    enter a class name... CMyFrameWnd
    CWnd Constructor
    CFrameWnd Constructor
    CMyFrameWnd Constructor
    Hello CMyFrameWnd
    enter a class name... CMyDoc
    CMyDoc Constructor
    Hello CMyDoc
    enter a class name... CWinApp
    Error: Trying to create object which is not DECLARE_DYNCREATE
    or DECLARE_SERIAL: CWinApp.
    enter a class name... CJjhou (故意輸入&#12690;個不在「類別型錄網」&#12695;的類別名稱)
    Error: Class not found: CJjhou (程式結束)
      

  7.   

    MFC.H
    #0001 #define BOOL int
    #0002 #define TRUE 1
    #0003 #define FALSE 0
    #0004 #define LPCSTR LPSTR
    #0005 typedef char* LPSTR;
    #0006 #define UINT int
    #0007 #define PASCAL _stdcall
    #0008 #define TRACE1 printf
    #0009
    #0010 #include <iostream.h>
    #0011 #include <stdio.h>
    #0012 #include <string.h>
    #0013
    #0014 class CObject;
    #0015
    #0016 struct CRuntimeClass
    #0017 {
    #0018 // Attributes
    #0019 LPCSTR m_lpszClassName;
    #0020 int m_nObjectSize;
    #0021 UINT m_wSchema; // schema number of the loaded class
    #0022 CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
    #0023 CRuntimeClass* m_pBaseClass;
    #0024
    #0025 CObject* CreateObject();
    #0026 static CRuntimeClass* PASCAL Load();
    #0027
    #0028 // CRuntimeClass objects linked together in simple list
    #0029 static CRuntimeClass* pFirstClass; // start of class list
    #0030 CRuntimeClass* m_pNextClass; // linked list of registered classes
    #0031 };
    #0032
    #0033 struct AFX_CLASSINIT
    #0034 { AFX_CLASSINIT(CRuntimeClass* pNewClass); };
    #0035
    #0036 #define RUNTIME_CLASS(class_name) #0037 (&class_name::class##class_name)
    #0038
    #0039 #define DECLARE_DYNAMIC(class_name) #0040 public: #0041 static CRuntimeClass class##class_name; #0042 virtual CRuntimeClass* GetRuntimeClass() const;
    #0043
    #0044 #define DECLARE_DYNCREATE(class_name) #0045 DECLARE_DYNAMIC(class_name) #0046 static CObject* PASCAL CreateObject();
    #0047
    #0048 #define _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) #0049 static char _lpsz##class_name[] = #class_name; #0050 CRuntimeClass class_name::class##class_name = { #0051 _lpsz##class_name, sizeof(class_name), wSchema, pfnNew, #0052 RUNTIME_CLASS(base_class_name), NULL }; #0053 static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name); #0054 CRuntimeClass* class_name::GetRuntimeClass() const #0055 { return &class_name::class##class_name; } #0056
    #0057 #define IMPLEMENT_DYNAMIC(class_name, base_class_name) #0058 _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)
    #0059
    #0060 #define IMPLEMENT_DYNCREATE(class_name, base_class_name) #0061 CObject* PASCAL class_name::CreateObject() #0062 { return new class_name; } #0063 _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, #0064 class_name::CreateObject)
    #0065
    #0066 class CObject
    #0067 {
    #0068 public:
    #0069 CObject::CObject() {
    #0070 }
    #0071 CObject::~CObject() {
    #0072 }
    #0073
    #0074 virtual CRuntimeClass* GetRuntimeClass() const;
    #0075 BOOL IsKindOf(const CRuntimeClass* pClass) const;
    #0076
    #0077 public:
    #0078 static CRuntimeClass classCObject;
    #0079 virtual void SayHello() { cout << "Hello CObject \n"; }
    #0080 };
    #0081
    #0082 class CCmdTarget : public CObject
    #0083 {
    #0084 DECLARE_DYNAMIC(CCmdTarget)
    #0085 public:
    #0086 CCmdTarget::CCmdTarget() {
    #0087 }
    #0088 CCmdTarget::~CCmdTarget() {
    #0089 }
    #0090 };
    #0091
    #0092 class CWinThread : public CCmdTarget
    #0093 {
    #0094 DECLARE_DYNAMIC(CWinThread)
    #0095 public:
    #0096 CWinThread::CWinThread() {
    #0097 }
    #0098 CWinThread::~CWinThread() {
    #0099 }
    #0100
    #0101 virtual BOOL InitInstance() {
    #0102 return TRUE;
    #0103 }
    #0104 virtual int Run() {
    #0105 return 1;
    #0106 }
    #0107 };
    #0108
    #0109 class CWnd;
    #0110
    #0111 class CWinApp : public CWinThread
    #0112 {
    #0113 DECLARE_DYNAMIC(CWinApp)
    #0114 public:
    #0115 CWinApp* m_pCurrentWinApp;
    #0116 CWnd* m_pMainWnd;
    #0117
    #0118 public:
    #0119 CWinApp::CWinApp() {
    #0120 m_pCurrentWinApp = this;
    #0121 }
    #0122 CWinApp::~CWinApp() {
    #0123 }
    #0124
    #0125 virtual BOOL InitApplication() {
    #0126 return TRUE;
    #0127 }
    #0128 virtual BOOL InitInstance() {
    #0129 return TRUE;
    #0130 }
    #0131 virtual int Run() {
    #0132 return CWinThread::Run();
    #0133 }
    #0134 };
    #0135
    #0136
    #0137 class CDocument : public CCmdTarget
    #0138 {
    #0139 DECLARE_DYNAMIC(CDocument)
    #0140 public:
    #0141 CDocument::CDocument() {
    #0142 }
    #0143 CDocument::~CDocument() {
    #0144 }
    #0145 };
    #0146
    #0147 class CWnd : public CCmdTarget
    #0148 {
    #0149 DECLARE_DYNCREATE(CWnd)
    #0150 public:
    #0151 CWnd::CWnd() {
    #0152 cout << "CWnd Constructor \n";
    #0153 }
    #0154 CWnd::~CWnd() {
    #0155 }
    #0156
    #0157 virtual BOOL Create();
    #0158 BOOL CreateEx();
    #0159 virtual BOOL PreCreateWindow();
    #0160 void SayHello() { cout << "Hello CWnd \n"; }
    #0161 };
    #0162
    #0163 class CFrameWnd : public CWnd
    #0164 {
    #0165 DECLARE_DYNCREATE(CFrameWnd)
    #0166 public:
    #0167 CFrameWnd::CFrameWnd() {
    #0168 cout << "CFrameWnd Constructor \n";
    #0169 }
    #0170 CFrameWnd::~CFrameWnd() {
    #0171 }
    #0172 BOOL Create();
    #0173 virtual BOOL PreCreateWindow();
    #0174 void SayHello() { cout << "Hello CFrameWnd \n"; }
    #0175 };
    #0176
    #0177 class CView : public CWnd
    #0178 {
    #0179 DECLARE_DYNAMIC(CView)
    #0180 public:
    #0181 CView::CView() {
    #0182 }
    #0183 CView::~CView() {
    #0184 }
    #0185 };
    #0186
    #0187 // global function
    #0188 CWinApp* AfxGetApp();
      

  8.   

    MFC.CPP#0001 #include "my.h" // it should be mfc.h, but for CMyWinApp definition, so...
    #0002
    #0003 extern CMyWinApp theApp;
    #0004
    #0005 static char szCObject[] = "CObject";
    #0006 struct CRuntimeClass CObject::classCObject =
    #0007 { szCObject, sizeof(CObject), 0xffff, NULL, NULL };
    #0008 static AFX_CLASSINIT _init_CObject(&CObject::classCObject);
    #0009
    #0010 CRuntimeClass* CRuntimeClass::pFirstClass = NULL;
    #0011
    #0012 AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass)
    #0013 {
    #0014 pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
    #0015 CRuntimeClass::pFirstClass = pNewClass;
    #0016 }
    #0017
    #0018 CObject* CRuntimeClass::CreateObject()
    #0019 {
    #0020 if (m_pfnCreateObject == NULL)
    #0021 {
    #0022 TRACE1("Error: Trying to create object which is not "
    #0023 "DECLARE_DYNCREATE \nor DECLARE_SERIAL: %hs.\n",
    #0024 m_lpszClassName);
    #0025 return NULL;
    #0026 }
    #0027
    #0028 CObject* pObject = NULL;
    #0029 pObject = (*m_pfnCreateObject)();
    #0030
    #0031 return pObject;
    #0032 }
    #0033
    #0034 CRuntimeClass* PASCAL CRuntimeClass::Load()
    #0035 {
    #0036 char szClassName[64];
    #0037 CRuntimeClass* pClass;
    #0038
    #0039 // JJHOU : instead of Load from file, we Load from cin.
    #0040 cout << "enter a class name... ";
    #0041 cin >> szClassName;
    #0042
    #0043 for (pClass = pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)
    #0044 {
    #0045 if (strcmp(szClassName, pClass->m_lpszClassName) == 0)
    #0046 return pClass;
    #0047 }
    #0048
    #0049 TRACE1("Error: Class not found: %s \n", szClassName);
    #0050 return NULL; // not found
    #0051 }
    #0052
    #0053 CRuntimeClass* CObject::GetRuntimeClass() const
    #0054 {
    #0055 return &CObject::classCObject;
    #0056 }
    #0057
    #0058 BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
    #0059 {
    #0060 CRuntimeClass* pClassThis = GetRuntimeClass();
    #0061 while (pClassThis != NULL)
    #0062 {
    #0063 if (pClassThis == pClass)
    #0064 return TRUE;
    #0065 pClassThis = pClassThis->m_pBaseClass;
    #0066 }
    #0067 return FALSE; // walked to the top, no match
    #0068 }
    #0069
    #0070 BOOL CWnd::Create()
    #0071 {
    #0072 return TRUE;
    #0073 }
    #0074
    #0075 BOOL CWnd::CreateEx()
    #0076 {
    #0077 PreCreateWindow();
    #0078 return TRUE;
    #0079 }
    #0080
    #0081 BOOL CWnd::PreCreateWindow()
    #0082 {
    #0083 return TRUE;
    #0084 }
    #0085
    #0086 BOOL CFrameWnd::Create()
    #0087 {
    #0088 CreateEx();
    #0089 return TRUE;
    #0090 }
    #0091
    #0092 BOOL CFrameWnd::PreCreateWindow()
    #0093 {
    #0094 return TRUE;
    #0095 }
    #0096
    #0097 CWinApp* AfxGetApp()
    #0098 {
    #0099 return theApp.m_pCurrentWinApp;
    #0100 }
    #0101
    #0102 IMPLEMENT_DYNAMIC(CCmdTarget, CObject)
    #0103 IMPLEMENT_DYNAMIC(CWinThread, CCmdTarget)
    #0104 IMPLEMENT_DYNAMIC(CWinApp, CWinThread)
    #0105 IMPLEMENT_DYNAMIC(CDocument, CCmdTarget)
    #0106 IMPLEMENT_DYNCREATE(CWnd, CCmdTarget)
    #0107 IMPLEMENT_DYNAMIC(CView, CWnd)
    #0108 IMPLEMENT_DYNCREATE(CFrameWnd, CWnd)MY.H#0001 #include <iostream.h>
    #0002 #include "mfc.h"
    #0003
    #0004 class CMyWinApp : public CWinApp
    #0005 {
    #0006 public:
    #0007 CMyWinApp::CMyWinApp() {
    #0008 }
    #0009 CMyWinApp::~CMyWinApp() {
    #0010 }
    #0011
    #0012 virtual BOOL InitInstance();
    #0013 };
    #0014
    #0015 class CMyFrameWnd : public CFrameWnd
    #0016 {
    #0017 DECLARE_DYNCREATE(CMyFrameWnd)
    #0018 public:
    #0019 CMyFrameWnd();
    #0020 ~CMyFrameWnd() {
    #0021 }
    #0022 void SayHello() { cout << "Hello CMyFrameWnd \n"; }
    #0023 };
    #0024
    #0025 class CMyDoc : public CDocument
    #0026 {
    #0027 DECLARE_DYNCREATE(CMyDoc)
    #0028 public:
    #0029 CMyDoc::CMyDoc() {
    #0030 cout << "CMyDoc Constructor \n";
    #0031 }
    #0032 CMyDoc::~CMyDoc() {
    #0033 }
    #0034 void SayHello() { cout << "Hello CMyDoc \n"; }
    #0035 };
    #0036
    #0037 class CMyView : public CView
    #0038 {
    #0039 DECLARE_DYNCREATE(CMyView)
    #0040 public:
    #0041 CMyView::CMyView() {
    #0042 cout << "CMyView Constructor \n";
    #0043 }
    #0044 CMyView::~CMyView() {
    #0045 }
    #0046 void SayHello() { cout << "Hello CMyView \n"; }
    #0047 };
    #0048
    #0049 // global function
    #0050 void AfxPrintAllClasses();MY.CPP#0001 #include "my.h"
    #0002
    #0003 CMyWinApp theApp;
    #0004
    #0005 BOOL CMyWinApp::InitInstance()
    #0006 {
    #0007 m_pMainWnd = new CMyFrameWnd;
    #0008 return TRUE;
    #0009 }
    #0010
    #0011 CMyFrameWnd::CMyFrameWnd()
    #0012 {
    #0013 cout << "CMyFrameWnd Constructor \n";
    #0014 Create();
    #0015 }
    #0016
    #0017 IMPLEMENT_DYNCREATE(CMyFrameWnd, CFrameWnd)
    #0018 IMPLEMENT_DYNCREATE(CMyDoc, CDocument)
    #0019 IMPLEMENT_DYNCREATE(CMyView, CView)
    #0020
    #0021 void PrintAllClasses()
    #0022 {
    #0023 CRuntimeClass* pClass;
    #0024
    #0025 // just walk through the simple list of registered classes
    #0026 for (pClass = CRuntimeClass::pFirstClass; pClass != NULL;
    #0027 pClass = pClass->m_pNextClass)
    #0028 {
    #0029 cout << pClass->m_lpszClassName << "\n";
    #0030 cout << pClass->m_nObjectSize << "\n";
    #0031 cout << pClass->m_wSchema << "\n";
    #0032 }
    #0033 }
    #0034 //---------------------------------------------------------------
    #0035 // main
    #0036 //---------------------------------------------------------------
    #0037 void main()
    #0038 {
    #0039 CWinApp* pApp = AfxGetApp();
    #0040
    #0041 pApp->InitApplication();
    #0042 pApp->InitInstance();
    #0043 pApp->Run();
    #0044
    #0045 //Test Dynamic Creation
    #0046 CRuntimeClass* pClassRef;
    #0047 CObject* pOb;
    #0048 while(1)
    #0049 {
    #0050 if ((pClassRef = CRuntimeClass::Load()) == NULL)
    #0051 break;
    #0052
    #0053 pOb = pClassRef->CreateObject();
    #0054 if (pOb != NULL)
    #0055 pOb->SayHello();
    #0056 }
    #0057 }
      

  9.   

    感谢大家的热情参与.不过我先声明一点,我对IMPLEMENT_DYNAMIC/DECLARE_DYNAMIC的意义和作用已经非常的了解,而且自信比相当一部分人都清楚它们的价值与用法,甚至实现原理,就算是闭着眼也知道这个宏定义的具体实现里有些什么东西,都是干什么的.我的问题仅仅只是,MFC的实现者为什么不像通常我们自己实现的类那样,把IMPLEMENT_DYNAMIC(CView,CWnd)放在把实现CView的文件ViewCore.cpp中,而放在实现框架类的文件WinFrm.cpp中,而且却在这里放了个与这个文件的主作用无关的IMPLEMENT_DYNAMIC(CSplitterWnd, CWnd).各位一定注意到后面的注释"for swap tuning(用于交换调整)"了吧----至少它表明MFC绝对不是无意把它放在这的.murouwolf(传说中弱弱的菜鸟(想女人想疯了...)) :重申一次,这时问的不是这个宏的原理与实现.不必贴出那么裹脚布似的代码来赚人眼球.
    byf2002(调侃2002) :<深入浅出MFC>那本书没讲这个东西.它只讲了MFC的概念级的原理,从没对这种实现细节有过描述.事实上,它几乎自己实现了一个精简版的MFC,而不是在MS的实现上作的研究.
      

  10.   

    之所以放在WinFrm.cpp主要是因为doc/view结构。
    主要是各种对象view,doc,frame,childframe的交互问题。
    在很多对象中都要实现这个功能,而WinFrm.cpp对应的就是mainframe.这是构架决定的。
      

  11.   

    我猜是微软程序员A懒得动程序员B的代码,于是原样照搬过来的。 PM又看不懂A和B的代码,于是就成这样子了。于是就成这样子了 :)
      

  12.   

    我个人觉得是因为frm/doc/view这个三位一体中,frm具有老大的地位,有一种包含关系吧 :)
    所以实现定在frm.cpp里
      

  13.   

    说的都差不多了,MSDN的runtimeclass部分有介绍的
      

  14.   

    对,《深入浅出MFC》第二版中讲得非常详细了。楼主慷慨散分吧:)
      

  15.   

    楼上的发言都是文不对题。楼主问的是,为什么会把CSplitterWnd和CCtrlView的IMPLEMENT_DYNAMIC宏放在CView的文件里面,而不放在CSplitterWnd和CCtrlView各自对应的cpp文件里。这个问题,Wingo在他的《MFC Internals》的附录里专门提到了,因为Wingo对这样做也是非常不解,他写信问了MFC的总设计师才知道答案。具体怎么回事我也忘记了,当时在图书馆里只是扫了几眼,只记得和保护模式有关。因为x86保护模式下,每个段的属性字段中有一个段粒度位G(Granularity),G=0表示段界限以字节为单位,于是20位的界限可表示的范围是1字节至1M字节,增量为1字节;G=1表示段界限以4K字节为单位,于是20位的界限可表示的范围是4K字节至4G字节,增量为4K字节。所以当粒度为1时,段的界限实际上就扩展成32位。
    现在猜想,MFC这样做的原因,可能是为了限定单个Obj文件的大小或者按4k边界对齐。
      

  16.   

    WinZZ(没有鞋的人) :对个头哦,你在那本书上找得出答案,我另开贴送你200分.
    mscf(扎西特勒) :在MSDN的哪部分?我可是半天都没翻出来有这方面的内容.注意题目不是问这个宏的意义与怎么实现.
    whoo(谁) :能在微软总部混饭吃的人,不可能那么菜吧?就算不懂.而且如果是随意决定的位置,它就不会在后面加注释进行特别说明.
    codewarrior(会思考的草) :你说的好象是边界对齐(alignment)而不是粒度(Granularity)吧?
      

  17.   

    IMPLEMENT_DYNAMIC是使用输入输出流函数<<和>>,如果只是调用基类的Serialize函数,不需要IMPLEMENT_DYNAMIC和声明DECLARE_DYNAMIC,只需要满足别的几个条件就可以了,比如从CObject派生等,楼主对这个知道的,呵呵
      

  18.   

    具体我已经记不清了,但是我敢100%保证,这个问题在《MFC Internals》中有正确的解答。
      

  19.   

    我见那么多人发深入浅出mfc,我好心发上来,却被当裹脚布:(,不对嘛也可以翻翻嘛,不要用来檫脚撒
      

  20.   

    codewarrior(会思考的草) :跑了文轩连锁的好几个分店,终于在成都购书中心翻出了那本压在箱子底都快发黄了的<MFC Internals>,上面果然有答案,不过跟你上面所说的完全是两回事!还是向你致敬,毕竟通往金字塔顶的路是你给的哈.NND文轩连锁,四川最大的书商,连新华书店都被它吞了的,居然不提供书目查询,还美其名曰电脑故障,555答案不敢独享,有请大家一起欣赏MFC的权威DeanMcCrory院士的解释:注释中的swap tuning(交换调整)的本意是在DLL链接完成后重新组织页面,从而使产生的woking set(工作集)更小,但在这里它的意义调整IMPLEMENT_DYNAMIC宏的位置从而减小上一行注释中所说的.Obj granularity(.OBJ粒度,即lib granularity,库粒度).在静态链接时,MFC开发者希望它是一个pay as you go的系统,即用户所用MFC特性越多,链接进的MFC代码越多,用得越少,链接的代码越少.
    大多数情况下,链接器和编译器合作完成这些.用/Gy参数编译,每个函数都会放到自己的comdat(对链接器来相当于一个独立的OBJ文件)中.然后用/opt:noref链接,这将削减那些那些从未引用过的comdat,最终使只有引用到的函数的代码少会出现在链接结果中.
    但是,当用户有指向函数的non-comdat部分时会出现问题.这些non-comdat部分包括消息表(包含消息处理函数的地址)和类似CRuntimeClass对象的数据(它包含CreateObject函数的地址,该函数会调用用构造函数,构造函数会重新设置vtable(虚表)中的虚函数地址,通过vtable会找到一些用户并没有用到的虚函数).
    有可能用户会引用对象的CRuntimeClass----可能仅在DECLARE_DYNAMIC中.如果这样的CRuntimeClass对象的定义和该类的其它实现代码在同一个文件,对象中的数据(如消息映射表)指向这个类中的函数,最终会将所有这样的函数的放入OBJ文件,不管它们有没有被用户引用----它们只被一些不被用户引用的数据所引用.但因它们所在的comdat有用户实际引用到的函数,不会被削减,结果产生大量不必要的代码.在CView的例子中,CFrameWnd需要使用了RUNTIME_CLASS(CView),因此使用CFrameWnd的代码都需要CRuntimeClass对象,如果视图类的CRuntimeClass对象的定义放在视图类的主实现文件ViewCore.cpp中,则仅使用CFrameWnd的程序就会因包含视图类的函数而变得更大.
    微软的解决方案是取用精心设计的示例,在链接后检查生成的MAP文件确定是否有不必要的内容加入进去,据此进行代码实现位置的交换调整数,它们仅做了少量的变动,对库粒度却有了很大的改进.主要参考了<MFC Internals>,George Shepherd,Scot Wingo,这本书的其它部分也相当精彩,不过它引用MFC的好象是4.2以前的版本,有些内容已经和当前版本不符了.
      

  21.   

    这本书确实还不错,涉及面还算够广,但是深度不够。可以充当分析MFC源码的“指南针”,迅速抓住要害代码加以分析,不会迷失在MFC数十万行洋洋洒洒的源码海洋中。
    记得我当时分析MFC的Dock/Float特性,绕了好多弯路,把MFC这部分源码看完了,才得到这本书,可惜它里面讲的对我而言已经没有价值了,用楼主的话,就是“闭着眼睛也能知道”了,不过如果一开始就看这本书的话,会省力不少,所以还是建议大家常备,呵呵。