BEGIN_MESSAGE_MAP / ON_COMMAND / END_MESSAGE_MAP 經預處理器展開後,生成:
AFX_MSGMAP_ENTRY _messageEntries[],每項包含:消息ID、控件ID、通知碼、處理函數指針、參數類型標識。AFX_MSGMAP messageMap = { &ParentClass::messageMap, _messageEntries },鏈接父類映射表,形成繼承鏈。virtual const AFX_MSGMAP* GetMessageMap() const 返回當前類映射表地址。| 方案 | 缺點 | 宏映射優勢 |
|---|---|---|
虛函數重寫 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++ 無反射時代的**最優工程妥協**。