传统的 C++ 编程中,通常使用整数来保存 enum 的逻辑运算结果 (与、或、非、异或等),在进行逻辑运算的时候没有进行类型检查,一个枚举类型可以和其他的枚举类型进行逻辑运算,运算的结果可以直接传递给接收参数为整数的函数。
Qt 中,模板类 QFlags<Enum>
提供了类型安全的方式保存 enum 的逻辑运算结果解决上面的这几个问题,这种方式在 Qt 里很常见,例如设置 QLabel 对齐方式的函数是 QLabel::setAlignment(Qt::Alignment)
(typedef QFlags<Qt::AlignmentFlag> Qt::Alignment
),这就意味着传给 setAlignment 的参数只能是枚举 Qt::AlignmentFlag
的变量、它们的逻辑运算结果或者 0,如果传入其他的枚举类型或者非 0 值,编译时就会报错:
1 2 3 4
| label->setAlignment(0); label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
label->setAlignment(Qt::WA_Hover);
|
想要把我们定义的枚举类型和 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <QDebug>
enum class Flag { Js = 0x01, Go = 0x02, Cpp = 0x04, Php = 0x08, Java = 0x10, Scala = 0x20,
}; Q_DECLARE_FLAGS(Flags, Flag) Q_DECLARE_OPERATORS_FOR_FLAGS(Flags)
int main() { Flags flags(0x08); flags |= Flag::Js; flags |= Flag::Go; flags.setFlag(Flag::Cpp, true); flags.setFlag(Flag::Cpp, false);
qDebug() << flags; qDebug() << flags.testFlag(Flag::Js); qDebug() << flags.testFlag(Flag::Cpp); qDebug() << int(flags);
flags = Flag::Java | Flag::Scala; qDebug() << flags;
return 0; }
|
在日常开发中,权限也可以使用 QFlags 来实现,下面只演示 3 种权限的使用,实际开发中根据具体业务需求修改枚举类型 Permission 的变量即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| #include <QDebug> #include <QMap> #include <QStringList>
enum class Permission { Readable = 0x01, Writable = 0x02, Excutable = 0x04, };
Q_DECLARE_FLAGS(Permissions, Permission) Q_DECLARE_OPERATORS_FOR_FLAGS(Permissions)
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(); qDebug() << user.getPermissionLabels(); qDebug() << user.hasPermission(Permission::Readable); qDebug() << user.hasPermission(Permission::Writable);
user.addPermission(Permission::Writable); qDebug() << user.getPermissions(); qDebug() << int(user.getPermissions());
user.removePermissions(Permission::Readable | Permission::Excutable); qDebug() << user.getPermissionLabels();
return 0; }
|