在 C++ 编程中,经常需要用枚举类型表示各种选项, 而这些选项还可能需要各种组合。 比如在 Qt 编程中, 需要经常写这样的代码。
MainWindow w;
w.setWindowFlags( Qt::WindowMinMaxButtonsHint | Qt::WindowMaxmizeButtonHint );
这要求枚举类型可以参与位运算, 并且位运算得到的结果还得是枚举类型。 也就是说, 我们需要满足这样的需求:
class AClass{
public:
enum TestFlag
{
Enum1 = 1, Enum2 = 2, Enum3 = 3
};
};
void EnumTest::testFlags()
{
AClass::TestFlags result = AClass::Enum1 | AClass::Enum2;
assert(result == (1 | 2));
}
先来看看原生 C++ 是否支持这种运算。 如果在 Qt Creator 里写出这段代码, Clang 静态语法分析会提示:
error: cannot initialize a variable of type 'AClass::TestFlag' with an rvalue of type 'int'
这说明, 在参与位运算时, TestFlag
枚举型变量被转型为 int
类型。 但是没有从 int
类型转换为TestFlag
类型的方法。
原生 C++ 的枚举类型还有一个问题, 那就是可以把不同的枚举变量放在一起运算。 请看以下代码:
class AClass{
public:
enum TestFlag
{
Enum1 = 1, Enum2 = 2, Enum3 = 3
};
};
class BClass
{
public:
enum TestFlags
{
Enum1, Enum2, Enum3
};
};
void EnumTest::testFlags()
{
int result = AClass::Enum1 | BClass::Enum2;
}
这个程序是可以通过编译的, 但是枚举值都是带有一定语义的,result
到底代表了什么含义呢? 这样做讲不出一点道理。
为了解决上面的问题,需要自己设计一个枚举类来增强 C++ 语言。 大概的思路是, 在枚举类型外套上一层,并进行运算符重载。
template <typename Enum>
class MqFlags
{
using Self = MqFlags<Enum>;
using EnumType = Enum;
public:
MqFlags(const MqFlags& other)
: i (other.i)
{
}
MqFlags(const Enum& e)
: i((int)e)
{
}
MqFlags(const int ival)
: i(ival)
{
}
operator int() const
{
return i;
}
MqFlags operator | (const MqFlags& another)
{
MqFlags g;
g.i = another.i | Self::i;
return g;
}
MqFlags operator | (const EnumType f)
{
return *this | MqFlags(f);
}
MqFlags operator & (const MqFlags& another)
{
MqFlags g;
g.i = another.i & Self::i;
return g;
}
MqFlags operator & (const EnumType f)
{
return *this & MqFlags(f);
}
private:
int i;
};
这里, int i
用于保存枚举类型变量的值,并提供了多种构造函数用于构造,还提供了一个转换函数, 把枚举变量转换为 int
类型变量。 这个类提供了一些算符运算, 用于支持简单的位运算。
在项目中使用这个改进的枚举变量, 除了声明原生枚举变量外, 还需要声明封装类。
class AClass{
public:
enum TestFlag
{
Enum1 = 1, Enum2 = 2, Enum3 = 3
};
using TestFlags = MqFlags <TestFlag>;
private:
TestFlags option;
};
接下来, 使用宏来化简这个封装类的声明。
#define MQ_DECLARE_FLAGS(enumType, newEnumType) using newEnumType = MqFlags<enumType>
class AClass{
public:
enum TestFlag
{
Enum1 = 1, Enum2 = 2, Enum3 = 3
};
MQ_DECLARE_FLAGS(TestFlag, TestFlags);
private:
TestFlags option;
};
这就是 Q_FLAGS
的实现细节, 我在源码上做了一些化简。 这样, 在有些不引用 qt 库的项目中, 也可以用到这个类、