传统的 C++ 编程中,通常使用整数来保存 enum 的逻辑运算结果 (与、或、非、异或等),在进行逻辑运算的时候没有进行类型检查,一个枚举类型可以和其他的枚举类型进行逻辑运算,运算的结果可以直接传递给接收参数为整数的函数。

Qt 中,模板类 QFlags<Enum> 提供了类型安全的方式保存 enum 的逻辑运算结果解决上面的这几个问题,这种方式在 Qt 里很常见,例如设置 QLabel 对齐方式的函数是 QLabel::setAlignment(Qt::Alignment) (typedef QFlags<Qt::AlignmentFlag> Qt::Alignment),这就意味着传给 setAlignment 的参数只能是枚举 Qt::AlignmentFlag 的变量、它们的逻辑运算结果或者 0,如果传入其他的枚举类型或者非 0 值,编译时就会报错:

label->setAlignment(0);
label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
label->setAlignment(Qt::WA_Hover); // Error: 编译时报错

想要把我们定义的枚举类型和 QFlags 一起使用,需要用到两个宏:

  • Q_DECLARE_FLAGS(Flags, Enum):

    • Enum 是已经定义好的枚举类型
    • 展开的结果为 typedef QFlags<Enum> Flags
  • Q_DECLARE_OPERATORS_FOR_FLAGS(Flags):
    • Flags 就是类型 QFlags<Enum>
    • 给 Flags 定义了运算符 |,使得 Enum 和 Enum,Enum 和 Flags 能够使用或运算符 |,结果为 Flags

使用 QFlags 时需要留意以下几点:

  • QFlags 其实就是用于位操作,设置它保存的数值的某一位为 1 或者为 0,所以和 QFlags 一起使用的枚举类型,其变量的值需要是 2 的 n 次方,即 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, …,它们的特点是其二进制位中只有一位是 1,其他位全部为 0,这样每一个枚举值只会和 QFlags 中的某一个位对应,不会出现交叉的情况
  • 调用函数 QFlags::setFlag(Enum flag, bool on = true),on 为 true 时设置 flag 对应的位为 1,on 为 false 时设置 flag 对应的位为 0,设置 flag 对应的位为 1 还可以使用运算符 |
  • 调用函数 QFlags::testFlag(Enum flag) 测试 flag 对应的位是否为 1
  • 整数转为 QFlags: 把整数作为 QFlags 构造函数的参数创建一个 QFlags 变量
  • QFlags 转为整数: 调用 int(flags) 把 QFlags 变量转换为整数值

下面就演示一下怎么使用 QFlags:

// Flag 的变量值是 2^n, 每个值的二进制只有一个位是 1,其他全为 0
enum class Flag {Js    = 0x01, //  1 : 0000 0001Go    = 0x02, //  2 : 0000 0010Cpp   = 0x04, //  4 : 0000 0100Php   = 0x08, //  8 : 0000 1000Java  = 0x10, // 16 : 0001 0000Scala = 0x20, // 32 : 0010 0000};
Q_DECLARE_FLAGS(Flags, Flag)
Q_DECLARE_OPERATORS_FOR_FLAGS(Flags) // 使得 Flag 和 Flag,Flag 和 Flags 能够使用或运算符 |,结果为 Flagsint main() {Flags flags(0x08);               // int to Flagsflags |= Flag::Js;               // 添加 Jsflags |= Flag::Go;               // 添加 Goflags.setFlag(Flag::Cpp, true);  // 添加 Cppflags.setFlag(Flag::Cpp, false); // 删除 CppqDebug() << flags;                     // 输出: QFlags(0x1|0x2|0x8)qDebug() << flags.testFlag(Flag::Js);  // 输出: trueqDebug() << flags.testFlag(Flag::Cpp); // 输出: falseqDebug() << int(flags);                // 输出: 11 (Flags to int)flags = Flag::Java | Flag::Scala; // 使用 | 同时设置多个 flagqDebug() << flags;                // 输出: QFlags(0x10|0x20)// flags = Flag::Java | Qt::AlignLeft; // Error: no viable overloaded =return 0;
}

在日常开发中,权限也可以使用 QFlags 来实现,下面只演示 3 种权限的使用,实际开发中根据具体业务需求修改枚举类型 Permission 的变量即可:

#include <QMap>
#include <QStringList>// 权限
enum class Permission {Readable  = 0x01,Writable  = 0x02,Excutable = 0x04,
};Q_DECLARE_FLAGS(Permissions, Permission)
Q_DECLARE_OPERATORS_FOR_FLAGS(Permissions)// 权限的值和其对应的 label 的 map
QMap<Permission, QString> PermissionLabelMap {{ Permission::Readable,  "可读" },{ Permission::Writable,  "可写" },{ Permission::Excutable, "可执行" },
};// 用户
class User {
public:User(const QString &username, int psValue) {this->username = username;this->ps = Permissions(psValue);}bool hasPermission(Permission p) const {return ps.testFlag(p);}void addPermission(Permission p) {ps.setFlag(p, true);}void addPermissions(Permissions ps) {this->ps |= ps;}void removePermission(Permission p) {ps.setFlag(p, false);}void removePermissions(Permissions ps) {this->ps &= Permissions(~(int(ps)));}Permissions getPermissions() const {return ps;}QStringList getPermissionLabels() const {QStringList labels;for (auto i = PermissionLabelMap.cbegin(); i != PermissionLabelMap.cend(); ++i) {if (ps.testFlag(i.key())) {labels << i.value();}}return labels;}private:Permissions ps;QString username;
};int main() {User user("Bob", 0);user.addPermissions(Permission::Readable | Permission::Excutable);qDebug() << user.getPermissions();                    // 输出: QFlags(0x1|0x4)qDebug() << user.getPermissionLabels();               // 输出: ("可读", "可执行")qDebug() << user.hasPermission(Permission::Readable); // 输出: trueqDebug() << user.hasPermission(Permission::Writable); // 输出: falseuser.addPermission(Permission::Writable);qDebug() << user.getPermissions();      // 输出: QFlags(0x1|0x2|0x4)qDebug() << int(user.getPermissions()); // 输出: 7user.removePermissions(Permission::Readable | Permission::Excutable);qDebug() << user.getPermissionLabels(); // 输出: ("可写")return 0;
}

又例如:

class CComGeneralTableWidget : public QTableWidget
{Q_OBJECTpublic:CComGeneralTableWidget(QWidget *parent = Q_NULLPTR);~CComGeneralTableWidget();/* 显示属性枚举(注意:id、设计id、可见性、显示名是所有元件都有且必须在属性对话框中显示的,这里就不再列出了,这里仅仅是列出有些元件需要显示的,有些元件不需要显示的,通过枚举值来控制哪些显示,哪些不显示),枚举值必须为0、2的N次方(N >= 0)*/enum class EmShowProp{eNotShowAllProp = 0,            // 画笔颜色、画笔宽度、画刷颜色、线型、旋转信息都不显示eShowRotateProp = 1,            // 显示旋转信息eShowPenColorProp = 2,          // 显示画笔颜色eShowPenWidthProp = 4,          // 显示画笔宽度eShowBrushColorProp = 8,        // 显示画刷颜色eShowLineTypeProp = 16,          // 显示线型// 显示所有eShowAllProp = eShowRotateProp | eShowPenColorProp | eShowPenWidthProp | eShowBrushColorProp | eShowLineTypeProp};Q_DECLARE_FLAGS(EmShowProps, EmShowProp)....// 类的其它代码
private:EmShowProps m_eShowProp{ EmShowProp::eShowAllProp };
};Q_DECLARE_OPERATORS_FOR_FLAGS(CComGeneralTableWidget::EmShowProps)

注意:通过上述Q_DECLARE_FLAGS声明后, 定义枚举不再是EmShowProp(最原始的用enum声明的、宏的第二个参数)而是 EmShowProps(宏的第一个参数),如果还用enum声明的枚举名定义枚举变量,则会报如下错误:

error C2228: “.testFlag”的左边必须有类/结构/联合

Qt中的测试 枚举与 QFlags详解相关推荐

  1. (17)System Verilog枚举类型enum详解

    (17)System Verilog枚举类型enum详解 1.1 目录 1)目录 2)FPGA简介 3)System Verilog简介 4)System Verilog枚举类型enum详解 5)结语 ...

  2. QT QSpinBox 整数计数器控件 使用详解

    本文详细的介绍了QSpinBox控件的各种操作,例如:获取数值.设置前后缀.设置最大/小值.进制转换.关联信号槽.优化信号.QSS优化.文件源码.样式表 .效果:可以设置背景.边框.向上按钮.向下按钮 ...

  3. yii mysql 事务处理_Yii2中事务的使用实例代码详解

    前言 一般我们做业务逻辑,都不会仅仅关联一个数据表,所以,会面临事务问题. 数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全 ...

  4. Python中的__name__和__main__含义详解

    背景 在写Python代码和看Python代码时,我们常常可以看到这样的代码: ? 1 2 3 4 5 def main():     ...... if __name__ == "__ma ...

  5. Java6.0中Comparable接口与Comparator接口详解

    Java6.0中Comparable接口与Comparator接口详解 说到现在,读者应该对Comparable接口有了大概的了解,但是为什么又要有一个Comparator接口呢?难道Java的开发者 ...

  6. 教程-Delphi中Spcomm使用属性及用法详解

    Delphi中Spcomm使用属性及用法详解 Delphi是一种具有 功能强大.简便易用和代码执行速度快等优点的可视化快速应用开发工具,它在构架企业信息系统方面发挥着越来越重要的作用,许多程序员愿意选 ...

  7. java 泛型详解、Java中的泛型方法、 java泛型详解

    本文参考java 泛型详解.Java中的泛型方法. java泛型详解 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即& ...

  8. java break 在if 中使用_java中使用国密SM4算法详解

    前言 上次总结了一下加密算法的分类(加密算法有集中形式,各有什么不同?),现在我们用java语言实现一下SM4:无线局域网标准的分组数据算法.对称加密,密钥长度和分组长度均为128位. ps:我们既可 ...

  9. python支持向量机回归_Python中支持向量机SVM的使用方法详解

    除了在Matlab中使用PRTools工具箱中的svm算法,Python中一样可以使用支持向量机做分类.因为Python中的sklearn库也集成了SVM算法,本文的运行环境是Pycharm. 一.导 ...

最新文章

  1. linux更改文件夹权限_Linux 一些重点知识,整理的很全面,有必要收藏
  2. 在2020年到来之前,你应该知道的10大科技趋势预测
  3. doT.js-doT模板方便快捷的组织页面DOM
  4. php session不生效_php 验证session无效问题解决办法
  5. proc_open 命令包含“有小问题
  6. PMcaff课堂:10年经验的产品大咖眼中的社交产品是这样的
  7. leetcode 558. Logical OR of Two Binary Grids Represented as Quad-Trees | 558. 四叉树交集(分治法)
  8. 代理模式和动态代理模式_代理模式介绍
  9. JAVA环境变量安装
  10. 安装运行jupyter notebook时报错:ModuleNotFoundError: No module named 'prompt_toolkit.formatted_text'...
  11. 有趣的问题:C的表达式x == x,何时为假?!
  12. HPnbsp;Jetdirectnbsp;打印服务器配置
  13. 链家混三个月底薪_应届毕业生入职链家,到离职
  14. 提交模式窗口后,刷新父窗口数据+获取frameset中各模块中数据
  15. css给网页添加 黑白滤镜
  16. strtok函数及其实现
  17. 根据GPS经纬度判断当前所属的市区
  18. 这个季节,想到了什么
  19. png 微软ppt 透明度,教你一招永久搞定PPT导出高清图片的小技巧
  20. matlab安装及使用

热门文章

  1. 人才空缺4600万!大厂优先录用,这个职业今年火遍全网
  2. 华硕服务器显示模块,华硕远程管理模块 ASMB4-iKVM 华硕服务器主板专用 现货 IPMI...
  3. Natasha V1.3.6.0 的升级日志
  4. Qt在控制台输出中文的解决办法(转载)
  5. JAVA中的设计模式三(策略模式)
  6. cxf返回的报文,命名空间无前缀
  7. Win7 x64 PL/SQL 连接 Oralce 提示 Could not initialize %ORACLE_HOME%\bin\oci.dll
  8. 山寨今日头条的标题title效果
  9. 好书推荐之《活着》 隐私策略(Privacy policy)
  10. 2.6.24及以上版本内核裁剪后启动黑屏的解决办法