CMap 详解
CMap
是 MFC (Microsoft Foundation Classes) 库中的一个模板类,用于实现键值对的映射关系(类似哈希表或字典)。它提供了高效的数据存储和检索功能,适用于需要通过键快速查找值的场景。
基本模板参数
cpp
运行
template<
class KEY, // 键的类型
class ARG_KEY, // 传递键的参数类型(通常是引用)
class VALUE, // 值的类型
class ARG_VALUE // 传递值的参数类型(通常是引用)
>
class CMap : public CObject
常用方法
- 添加元素:
SetAt(ARG_KEY key, ARG_VALUE newValue)
- 查找元素:
Lookup(ARG_KEY key, VALUE& rValue)
- 删除元素:
RemoveKey(ARG_KEY key)
- 删除所有元素:
RemoveAll()
- 获取元素数量:
GetCount()
- 检查是否为空:
IsEmpty()
实际应用场景
- 缓存机制:存储计算结果,避免重复计算
- 数据映射:将 ID 映射到对应的对象或数据
- 配置管理:存储键值对形式的配置信息
- 事件处理:将事件 ID 映射到处理函数
使用举例
下面是一个完整的示例,展示如何使用 CMap
实现一个简单的联系人管理器:
#pragma once
#include <afxtempl.h>
// 联系人信息结构体
struct ContactInfo
{
CString name;
CString phone;
CString email;
ContactInfo() {}
ContactInfo(const CString& n, const CString& p, const CString& e)
: name(n), phone(p), email(e) {}
};
// 联系人管理器类
class CContactManager
{
private:
// 使用CMap存储联系人信息,键为ID,值为ContactInfo结构体
CMap<int, int, ContactInfo, ContactInfo&> m_contactMap;
int m_nextId; // 下一个可用ID
public:
CContactManager() : m_nextId(1) {}
~CContactManager() { m_contactMap.RemoveAll(); }
// 添加联系人
int AddContact(const CString& name, const CString& phone, const CString& email);
// 根据ID查找联系人
bool FindContact(int id, ContactInfo& info) const;
// 更新联系人信息
bool UpdateContact(int id, const CString& name, const CString& phone, const CString& email);
// 删除联系人
bool DeleteContact(int id);
// 获取联系人数量
int GetContactCount() const;
// 显示所有联系人
void DisplayAllContacts() const;
};
#include "stdafx.h"
#include "ContactManager.h"
#include <iostream>
// 添加联系人
int CContactManager::AddContact(const CString& name, const CString& phone, const CString& email)
{
int id = m_nextId++;
ContactInfo info(name, phone, email);
m_contactMap.SetAt(id, info);
return id;
}
// 根据ID查找联系人
bool CContactManager::FindContact(int id, ContactInfo& info) const
{
return m_contactMap.Lookup(id, info);
}
// 更新联系人信息
bool CContactManager::UpdateContact(int id, const CString& name, const CString& phone, const CString& email)
{
ContactInfo info;
if (m_contactMap.Lookup(id, info))
{
info.name = name;
info.phone = phone;
info.email = email;
m_contactMap.SetAt(id, info);
return true;
}
return false;
}
// 删除联系人
bool CContactManager::DeleteContact(int id)
{
return m_contactMap.RemoveKey(id) != 0;
}
// 获取联系人数量
int CContactManager::GetContactCount() const
{
return m_contactMap.GetCount();
}
// 显示所有联系人
void CContactManager::DisplayAllContacts() const
{
if (m_contactMap.IsEmpty())
{
std::cout << "没有联系人记录" << std::endl;
return;
}
POSITION pos = m_contactMap.GetStartPosition();
while (pos != NULL)
{
int id;
ContactInfo info;
m_contactMap.GetNextAssoc(pos, id, info);
// 转换CString为char*以便输出
CStringA name(info.name);
CStringA phone(info.phone);
CStringA email(info.email);
std::cout << "ID: " << id << std::endl;
std::cout << " 姓名: " << name << std::endl;
std::cout << " 电话: " << phone << std::endl;
std::cout << " 邮箱: " << email << std::endl;
std::cout << "------------------------" << std::endl;
}
}
#include "stdafx.h"
#include "ContactManager.h"
int main()
{
CContactManager manager;
// 添加联系人
int id1 = manager.AddContact(_T("张三"), _T("13800138001"), _T("zhangsan@example.com"));
int id2 = manager.AddContact(_T("李四"), _T("13900139001"), _T("lisi@example.com"));
int id3 = manager.AddContact(_T("王五"), _T("13700137001"), _T("wangwu@example.com"));
std::cout << "添加了 " << manager.GetContactCount() << " 个联系人" << std::endl;
// 显示所有联系人
std::cout << "\n所有联系人列表:" << std::endl;
manager.DisplayAllContacts();
// 查找联系人
ContactInfo info;
if (manager.FindContact(id2, info))
{
CStringA name(info.name);
std::cout << "\n找到ID为 " << id2 << " 的联系人: " << name << std::endl;
}
// 更新联系人
manager.UpdateContact(id2, _T("李四(更新)"), _T("13900139002"), _T("lisi_updated@example.com"));
// 删除联系人
manager.DeleteContact(id3);
// 显示更新后的联系人列表
std::cout << "\n更新后的联系人列表:" << std::endl;
manager.DisplayAllContacts();
return 0;
}
代码说明
上面的示例展示了如何使用 CMap
实现一个简单的联系人管理器:
-
数据结构设计:
- 定义了
ContactInfo
结构体存储联系人信息 - 使用
CMap<int, int, ContactInfo, ContactInfo&>
存储联系人数据
- 定义了
-
核心功能:
- 添加联系人:使用
SetAt()
方法将新联系人添加到映射表 - 查找联系人:使用
Lookup()
方法根据 ID 查找联系人 - 更新联系人:先查找,然后修改信息并重新设置
- 删除联系人:使用
RemoveKey()
方法删除指定 ID 的联系人 - 遍历所有联系人:使用
GetStartPosition()
和GetNextAssoc()
方法
- 添加联系人:使用
-
内存管理:
CMap
自动管理存储的对象,不需要手动释放- 析构函数中调用
RemoveAll()
确保所有资源被释放
使用 CMap 的注意事项
-
键类型要求:
- 键类型必须支持
==
运算符(用于比较) - 如果使用自定义类作为键,需要重载
==
运算符
- 键类型必须支持
-
内存管理:
- 对于值类型为指针的情况,需要手动管理指针指向的对象
- 建议在删除元素前先释放指针指向的内存
-
遍历操作:
- 遍历过程中不要修改映射表结构(添加或删除元素)
- 如果需要修改,应使用
GetNextAssoc()
返回的 POSITION 进行操作
-
性能考虑:
CMap
提供 O (1) 的平均查找时间- 但在最坏情况下可能退化为 O (n)
#if !defined(AFX_LINECOLORLISTCTRL_H__92EB1CD4_CD33_4E02_A098_367A8FEFF38E__INCLUDED_)
#define AFX_LINECOLORLISTCTRL_H__92EB1CD4_CD33_4E02_A098_367A8FEFF38E__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// LineColorListCtrl.h : header file
//
#include <afxtempl.h>
typedef struct
{
COLORREF colText; //前景色
COLORREF colTextBk;//背景色
} TEXT_BK;
/
// CLineColorListCtrl window
class CLineColorListCtrl : public CListCtrl
{
public:
void SetItemColor(DWORD iItem, COLORREF TextColor, COLORREF TextBkColor); //设置某一行的前景色和背景色
void SetAllItemColor(DWORD iItem, COLORREF TextColor, COLORREF TextBkColor);//指设置所有从第一行到iItem行的颜色。
void ClearColor(); //清除颜色映射表
int m_iPopMenu;
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
// Construction
public:
CLineColorListCtrl();
// Attributes
public:
CMap<DWORD, DWORD&, TEXT_BK, TEXT_BK&> MapItemColor;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CLineColorListCtrl)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CLineColorListCtrl();
// Generated message map functions
protected:
//{{AFX_MSG(CLineColorListCtrl)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
void CLineColorListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);
DECLARE_MESSAGE_MAP()
};
/
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_LINECOLORLISTCTRL_H__92EB1CD4_CD33_4E02_A098_367A8FEFF38E__INCLUDED_)
// LineColorListCtrl.cpp : implementation file
//
#include "stdafx.h"
#include "LineColorListCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CLineColorListCtrl
CLineColorListCtrl::CLineColorListCtrl()
{
m_iPopMenu = 0;
}
CLineColorListCtrl::~CLineColorListCtrl()
{
}
BEGIN_MESSAGE_MAP(CLineColorListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CLineColorListCtrl)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdraw)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
/
// CLineColorListCtrl message handlers
BOOL CLineColorListCtrl::OnEraseBkgnd(CDC* pDC)
{
return false;
return CListCtrl::OnEraseBkgnd(pDC);
}
//避免背景闪烁
void CLineColorListCtrl::OnPaint()
{
//响应WM_PAINT消息
CPaintDC dc(this); // device context for painting
CRect rect;
CRect headerRect;
CDC MenDC;//内存ID表
CBitmap MemMap;
GetClientRect(&rect);
GetDlgItem(0)->GetWindowRect(&headerRect);
MenDC.CreateCompatibleDC(&dc);
MemMap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
MenDC.SelectObject(&MemMap);
MenDC.FillSolidRect(&rect, RGB(228, 236, 243));
//这一句是调用默认的OnPaint(),把图形画在内存DC表上
DefWindowProc(WM_PAINT, (WPARAM)MenDC.m_hDC, (LPARAM)0);
//输出
dc.BitBlt(0, headerRect.Height(), rect.Width(), rect.Height(), &MenDC, 0, headerRect.Height(), SRCCOPY);
MenDC.DeleteDC();
MemMap.DeleteObject();
}
//自定义绘画颜色处理
void CLineColorListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
*pResult = CDRF_DODEFAULT;
NMLVCUSTOMDRAW * lplvdr = (NMLVCUSTOMDRAW*)pNMHDR;
NMCUSTOMDRAW &nmcd = lplvdr->nmcd;
switch(lplvdr->nmcd.dwDrawStage)//判断状态
{
case CDDS_PREPAINT:
{
*pResult = CDRF_NOTIFYITEMDRAW;
break;
}
case CDDS_ITEMPREPAINT://如果为画ITEM之前就要进行颜色的改变
{
// 根据在 SetItemColor(DWORD iItem, COLORREF color) 设置的
// ITEM号和COLORREF 在摸板中查找,然后进行颜色赋值。
TEXT_BK tb;
if(MapItemColor.Lookup(nmcd.dwItemSpec, tb))
{
lplvdr->clrText = tb.colText;
lplvdr->clrTextBk = tb.colTextBk;
*pResult = CDRF_DODEFAULT;
}
}
break;
}
}
void CLineColorListCtrl::ClearColor()
{
MapItemColor.RemoveAll();
}
void CLineColorListCtrl::SetItemColor(DWORD iItem, COLORREF TextColor, COLORREF TextBkColor)
{
TEXT_BK tb;
tb.colText = TextColor;
tb.colTextBk = TextBkColor;
MapItemColor.SetAt(iItem, tb);//设置某行的颜色。
this->RedrawItems(iItem, iItem);//重新染色
this->SetFocus(); //设置焦点
UpdateWindow();
}
void CLineColorListCtrl::SetAllItemColor(DWORD iItem, COLORREF TextColor, COLORREF TextBkColor)
{
TEXT_BK tb;
tb.colText = TextColor;
tb.colTextBk = TextBkColor;
if(iItem > 0)
{
for(DWORD numItem = 0; numItem < iItem ; numItem ++)
{
MapItemColor.SetAt(numItem, tb);
this->RedrawItems(numItem, numItem);
}
}
}
TEXT_BK 结构体和 MapItemColor 映射表
这部分代码定义了一个结构体和一个映射表,用于存储和管理列表项的颜色信息:
TEXT_BK 结构体
cpp
运行
typedef struct
{
COLORREF colText; // 前景色(文本颜色)
COLORREF colTextBk; // 背景色
} TEXT_BK;
这个结构体用于存储每个列表项的颜色属性:
colText
:文本颜色,使用 Windows API 的COLORREF
类型(通常是一个 32 位整数,表示 RGB 颜色值)colTextBk
:背景颜色
通过typedef
定义了这个结构体的别名TEXT_BK
,这样在代码中可以直接使用TEXT_BK
而不需要每次都写struct TEXT_BK
。
MapItemColor 映射表
cpp
运行
CMap<DWORD, DWORD&, TEXT_BK, TEXT_BK&> MapItemColor;
这行代码声明了一个 MFC 的CMap
模板类实例,用于存储列表项索引和颜色信息的映射关系:
- 第一个模板参数
DWORD
:键的类型,表示列表项的索引 - 第二个模板参数
DWORD&
:键的引用类型,用于高效传递参数 - 第三个模板参数
TEXT_BK
:值的类型,表示颜色信息 - 第四个模板参数
TEXT_BK&
:值的引用类型,用于高效传递参数
用途
这个映射表在CLineColorListCtrl
类中用于:
- 在
SetItemColor
和SetAllItemColor
方法中设置特定列表项的颜色 - 在
OnNMCustomdraw
方法中查找并应用颜色设置 - 在
ClearColor
方法中清除所有颜色设置