【QT】枚举常用宏(Q_ENUM,Q_FLAG,Q_DECLARE_FLAGS,Q_DECLARE_OPERATORS_FOR_FLAGS)

1. Q_ENUM宏 与 QMetaEnum类

1.1 Q_ENUM宏的作用

  1. 宏Q_ENUM会向元对象系统注册一个枚举类型

1.2 使用Q_ENUM注意的问题

  1. 使用Q_ENUM之前,必须在类中先声明Q_OBJECT或Q_GADGET宏。
  2. Q_ENUM(枚举类型)必须放在枚举声明之后,放在前面编译器会报错。
  3. Q_ENUM宏引入自Qt5.5版本,之前版本的Qt请使用Q_ENUMS宏,但Q_ENUMS宏不支持QMetaEnum::fromType()函数(这也是Q_ENUMS被弃用的原因)

1.3 在写有关枚举的代码时,我们可能遇到这种情况:需要用到枚举的字符串,该怎么办?

这时就需要用到 Q_ENUM宏 和 QMetaEnum类。
用Q_ENUM声明的枚举,其QMetaEnum注册在外部的QMetaObject中。在5.5版本之前可以使用QMetaObject获取QMetaEnum。

//before Qt5.5
QMetaObject object = MyEnum::staticMetaObject;  //MyEnum是当前类,staticMetaObject返回类的元对象
int index = object.indexOfEnumerator("Orientation");  //Orientation是枚举的类型
QMetaEnum metaEnum = object.enumerator(index);

也可以使用静态函数QMetaEnum::fromType()来获取QMetaEnum。

QMetaEnum metaEnum = QMetaEnum::fromType<MyEnum::Orientation>();  //MyEnum是当前类,Orientation是枚举的类型

通过QMetaEnum对象就可以获得枚举的一些信息以及枚举类型与字符串类型的转换。

1.4 下面通过一段简单的代码来说明Q_ENUM的作用

#include <QDebug>
#include <QDialog>
#include <QMetaEnum>

class MyEnum : public QDialog {
    Q_OBJECT

public:
    enum Orientation {
        Up = 1,
        Down = 2,
        Left = 3,
        Right = 4
    };
    Q_ENUM(Orientation)  //向元对象系统注册枚举类型

    MyEnum(QWidget* parent = 0);
    ~MyEnum();
};
#include "myenum.h"

MyEnum::MyEnum(QWidget* parent) :
    QDialog(parent) {
    //获取QMetaEnum对象的方法1:
    //QMetaObject object = MyEnum::staticMetaObject;  //before Qt5.5
	//int index = object.indexOfEnumerator("Orientation");  //Orientation是枚举的类型
	//QMetaEnum metaEnum = object.enumerator(index);
	
	//获取QMetaEnum对象的方法2:
    QMetaEnum metaEnum = QMetaEnum::fromType<MyEnum::Orientation>();  //通过静态函数fromType获取QMetaEnum对象

    //QMetaEnum会对所有的枚举进行下标编号,从0开始
    QString name = metaEnum.name();                   //枚举名称
    int count = metaEnum.keyCount();                  //枚举数量
    QString keyIndex = metaEnum.key(0);               //下标为0的key
    int valueIndex = metaEnum.value(0);               //下标为0的value
    QString Key = metaEnum.valueToKey(MyEnum::Left);  //通过value得到key
    int value = metaEnum.keyToValue("Left");          //通过key得到value

    qDebug() << "枚举的名称:" << name;
    qDebug() << "枚举的数量:" << QString::number(count);
    qDebug() << "index下标的key值:" << keyIndex;
    qDebug() << "index下标的Value值:" << QString::number(valueIndex);
    qDebug() << "value对应的key值:" << Key;
    qDebug() << "key值对应的Vaule:" << QString::number(value);
}

MyEnum::~MyEnum() {}

在这里插入图片描述
需要知道的是:QMetaEnum会对所有的枚举进行下标编号,从0开始。
       QMetaEnum对枚举的保存是通过key和value的方式进行保存和获取的。
以上代码可以看出Q_ENUM声明的枚举,通过QMetaEnum对象可以获得枚举的许多信息。例如枚举的名称、枚举的数量、枚举的key和value值等等。

2. Q_FLAG宏

2.1 Q_FLAG宏的作用

宏Q_FLAG会向元对象系统注册一个单一的标志类型。
使用宏Q_FLAG声明的枚举,其枚举值可以作为标志,并使用位或操作符(|)进行组合。

如果进行位或操作,那上面的代码中枚举值就不能那样定义,因为位或操作是按照二进制位进行“或运算”,其枚举值的定义需要按照位来定义(即不同的枚举值其二进制的每一位都不同)。例如:

enum Orientation {
        Up = 0x01,   //即0000...0001
        Down = 0x02, //即0000...0010
        Left = 0x04, //即0000...0100
        Right = 0x08 //即0000...1000
};

2.2 Q_DECLARE_FLAGS()宏的作用

Q_DECLARE_FLAGS(Flags, Enum)宏展开为:

 typedef QFlags<Enum> Flags;

QFlags<Enum>是一个模板类,其中Enum是枚举类型,QFlags用于存储枚举值的组合。

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

下面看一个例子:

  enum Orientation
    {
        Up = 1,   //即0000...0001
        Down = 2, //即0000...0010
        Left = 4, //即0000...0100
        Right = 8 //即0000...1000
    };
 
    enum Direction
    {
        horizontal = 1,
        vertical = 2
	};

这种操作编译器不会报错:

  	Orientation::Up | Direction::horizontal;  //两个不相关的枚举值做逻辑运算没有意义

对于上面的问题,应该怎么解决?
Qt 中,模板类 QFlags<Enum> 提供了类型安全的方式保存 enum 的逻辑运算结果,来解决上面的问题。

这种方式在 Qt 里很常见,例如设置 QLabel 对齐方式的函数是 QLabel::setAlignment(Qt::Alignment) (typedef QFlagsQt::AlignmentFlag Qt::Alignment),这就意味着传给 setAlignment 的参数只能是枚举 Qt::AlignmentFlag 的变量、它们的逻辑运算结果或者 0,如果传入其他的枚举类型或者非 0 值,编译时就会报错,例如:

label->setAlignment(0); // OK
label->setAlignment(Qt::AlignLeft | Qt::AlignTop); // OK

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

总之,Q_DECLARE_FLAGS(Flags, Enum)宏将普通结构体Enum重新定义成了一个可以自由进行位或操作的安全的结构体Flags。

2.3 Q_DECLARE_OPERATORS_FOR_FLAGS()宏的作用

  1. Q_DECLARE_OPERATORS_FOR_FLAGS(Flags)赋予了Flags一个全局操作符“|”,没有这个宏语句,Flags量之间进行与操作后的结果将是一个int值,而不是Flags值。这点特别重要。
  2. Q_DECLARE_OPERATORS_FOR_FLAGS必须定义在类外。
  3. Q_DECLARE_OPERATORS_FOR_FLAGS只提供了“或”操作,没有提供“与”“非”操作。
  4. Q_DECLARE_FLAGS和Q_DECLARE_OPERATORS_FOR_FLAGS都是和元对象系统无关的,可以脱离Q_FLAG单独使用,事实上这两个宏在Qt4就已经存在(不确定更早是否存在),而Q_FLAG是在Qt5.5版本才加入的。

2.4 演示代码

#include <QDebug>
#include <QDialog>
#include <QMetaEnum>
#include <QMetaObject>

class MyEnum : public QDialog {
    Q_OBJECT

public:
    enum Orientation {
        Up = 0x01,    //即0000...0001
        Down = 0x02,  //即0000...0010
        Left = 0x04,  //即0000...0100
        Right = 0x08  //即0000...1000
    };
    Q_ENUM(Orientation)
    Q_DECLARE_FLAGS(Orientations, Orientation)
    Q_FLAG(Orientations)

    MyEnum(QWidget* parent = 0);
    ~MyEnum();
};

Q_DECLARE_OPERATORS_FOR_FLAGS(MyEnum::Orientations)
#include "myenum.h"

MyEnum::MyEnum(QWidget* parent) :
    QDialog(parent) {
    QMetaEnum metaEnum = QMetaEnum::fromType<MyEnum::Orientation>();  //通过静态函数fromType获取QMetaEnum对象

    QString name = metaEnum.name();                                    //枚举名称
    int count = metaEnum.keyCount();                                   //枚举数量
    QString keyIndex = metaEnum.key(0);                                //下标为0的key
    int valueIndex = metaEnum.value(0);                                //下标为0的value
    QString Key = metaEnum.valueToKeys(MyEnum::Left | MyEnum::Right);  //通过value得到key
    int value = metaEnum.keysToValue("Up | Down");                     //通过key得到value

    qDebug() << "枚举的名称:" << name;
    qDebug() << "枚举的数量:" << QString::number(count);
    qDebug() << "index下标的key值:" << keyIndex;
    qDebug() << "index下标的Value值:" << QString::number(valueIndex);
    qDebug() << "value对应的key值:" << Key;
    qDebug() << "key值对应的Vaule:" << QString::number(value);
}

MyEnum::~MyEnum() {}

在这里插入图片描述

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值