以 Java 视角看 C++ 中的枚举类型会觉得它“非常原始”,Java 中的枚举本质是一个类类型,而 C++ 中的枚举本质就是一个整型数值,它们之间的功能性和灵活性落差极大。不过,简单也有简单的好处,C++ 中的 Enum 可以直接拿来做数组 / 容器的索引就是一个难得的方便之处,也是 C++ 中的一种编程技巧和最佳实践。
1. Enum 作为数组 / 容器索引
以下是来自 VNote 中的一个实际示例。项目中定义了一个预览类型的枚举:
enumSource{ImageLink,CodeBlock,MathBlock,MaxSource};Source 的命名不太好,它其实是预览类型种类,VNote 在 Markdown 的编辑窗口中支持原地预览(Inplace Preview)一些 Markdown 的元素以供用户实时检查输入的 Markdown 文本是否正确,支持原地预览的类型一共有三种:ImageLink、CodeBlock、MathBlock。
用户可以配置来决定三种类型中的哪几个可以被原地预览,然后在应用程序中有一个数据结构:PreviewSourceData,用于存储预览类型的一些数据:
structPreviewSourceData{boolm_enabled=false;TimeStamp m_timeStamp=0;// Images inserted in the document resource manager.QHash<QString,TimeStamp>m_images;};它有一个m_enabled字段,就是用来标记一种预览类型是否会被预览的。因为系统总种只有三种预览类型,所以,只有三个PreviewSourceData实例。因此,VNote 引入了一个成员变量:
QVector<PreviewSourceData>m_previewData并这样初始化它:
m_previewData=QVector<PreviewSourceData>(Source::MaxSource)// 实际是在初始化列表中初始化的然后在需要取出某一种预览类型的数据时,就用 Source 枚举值作为索引,例如:
m_previewData[Source::ImageLink]这一整套设计中有几个关键细节成就了这种以 Enum 作为数组 / 容器索引的做法:
- C++ 中的枚举值默认是从
0开始,刚好好是数组索引的起始值 - Source 特意额外定义了一个
MaxSource安排在最后,它的值又刚好是数据的 size
2. 和 Map 的比较
按理说,用 Map 存储这三种预览类型的数据也是合理的,甚至在其他语言里可能是第一选择:
QMap<Source, PreviewSourceData> m_previewData`在 C++ 中,以 Enum 作为数组 / 容器索引是一种轻量、讨巧的做法,主要的好处有:
- 下标访问,极快
- 不会出现 “键不存在” 的问题 (key 是枚举限定住的)
发是使用 Map 有这样几个劣势,不是大问题:
- Map 的检索没有使用下表检索快捷
- Map 需要额外空间存储 Key
- Map 需要应对 “键不存在” 的问题