首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

MFC 消息映射宏機制深度解析:為什麼要用宏實現?

     編輯:程式設計疑難解答

宏展開本質:靜態消息路由表

BEGIN_MESSAGE_MAP / ON_COMMAND / END_MESSAGE_MAP 經預處理器展開後,生成:

  1. 靜態數組 AFX_MSGMAP_ENTRY _messageEntries[],每項包含:消息ID、控件ID、通知碼、處理函數指針、參數類型標識。
  2. 靜態結構 AFX_MSGMAP messageMap = { &ParentClass::messageMap, _messageEntries },鏈接父類映射表,形成繼承鏈。
  3. 虛函數 virtual const AFX_MSGMAP* GetMessageMap() const 返回當前類映射表地址。

為什麼不用虛函數重寫 / switch-case?

方案缺點宏映射優勢
虛函數重寫 OnCmdMsg每增加一個消息需修改基類/派生類聲明,破壞開閉原則;繼承鏈深時調用開銷大聲明與實現分離,僅在 .cpp 添加宏項,零侵入頭文件
巨型 switch(case WM_...)代碼膨脹、難維護、無法跨類復用數據驅動,編譯期生成表,運行期 O(1) 查找(哈希/二分)
動態註冊 map<UINT, function>運行期構建開銷、需鎖同步、難序列化零運行期開銷,靜態鏈接進二進制,極致性能

宏的具體作用

  • 類型安全封裝:ON_COMMAND(id, fn) 自動轉換為 WM_COMMAND, id, 0, AfxSig_vv, (AFX_PMSG)(fn),隱藏繁雜參數類型轉換(AfxSig_vv、AfxSig_lpl 等)。
  • 繼承鏈自動串聯:messageMap.pBaseMap = &ParentClass::messageMap,派生類無需手動合併父類表。
  • 編譯期檢查:函數簽名不匹配(如參數個數錯)直接報編譯錯,而非運行期崩潰。

運行時查找流程

LRESULT CWnd::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam) {
    const AFX_MSGMAP* pMap = GetMessageMap();
    while (pMap) {
        const AFX_MSGMAP_ENTRY* pEntry = pMap->lpEntries;
        while (pEntry->nSig != AfxSig_end) {
            if (pEntry->nMessage == msg && 
                (pEntry->nID == 0 || pEntry->nID == LOWORD(wParam)) &&
                (pEntry->nCode == 0 || pEntry->nCode == HIWORD(wParam))) {
                return CallHandler(pEntry, this, wParam, lParam); // 統一分發入口
            }
            ++pEntry;
        }
        pMap = pMap->pBaseMap; // 向上查找父類
    }
    return DefWindowProc(msg, wParam, lParam); // 未處理交給系統
}

結論

消息映射宏是編譯期元編程產物,用靜態數組+繼承鏈替代動態分發,實現了零開銷、類型安全、聲明實現分離、繼承復用四大目標。看似「復雜」,實則是 C++ 無反射時代的**最優工程妥協**。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved