强类型枚举
枚举:分门别类与数值的名字
enum Gender {
Male, // 默认设置为0
FeMale // 默认设置为1
};
enum {
Male,
FeMale
};
#define Male 0
#define FeMale 1
// C++ 推荐的方式, 静态常量作用域可以被限制在文件内,但是会增加存储空间
const static int Male = 0;
const static int FeMale = 1;
允许匿名枚举的出现容易出现以下问题:
enum Type { General, Light, Medium, Heavy };
enum Category { General, Pistol, MachineGun, Cannon };
// 出现了全局名字
#include
namespace T {
enum Type { General, Light, Medium, Heavy };
};
// 匿名 namespace, Category 会默认进入全局命名空间
namespace {
enum Category { General, Pistol, MachineGun, Cannon };
};
int main() {
T::Type t = T::Light;
if (t == General) // 忘记使用 namespace
std::cout << "General Weapon" << std::endl;
return 0;
}
- C语言中枚举是 常量数值的 别名,因此会被隐式转换为整形。这是不安全的。
#include
using namespace std;
enum Type { General, Light, Medium, Heavy };
// enum Category { General, Pistol, MachineGun, Cannon }; // error, 重复定义
enum Category { Pistol, MachineGun, Cannon };
struct Killer {
Killer(Type t, Category c) : type(t), category(c) {}
Type type;
Category category;
};
int main()
{
Killer cool(General, MachineGun);
// ......
if (cool.type >= Pistol) {
std::cout << "It is not a pistol" << std::endl;
}
// ...
std::cout << is_pod::value << std::endl; // 1
std::cout << is_pos::value << std::endl; // 1
return 0;
}
为了解决上述问题,需要对 enum 进行封装.
#include
class Type {
public:
enum type { general, light, medium, heavy };
type val;
public:
Type(type t) : val(t) {}
bool operator >= (const Type &t) { return val >= t.val; }
static const Type General, Light, Medium, Heavy;
};
const Type Type::General(Type::general);
const Type Type::Light(Type::light);
const Type Type::Medium(Type::medium);
const Type Type::Heavy(Type::heavy);
class Category {
public:
enum category { pistol, machineGun, cannon };
category val;
public:
Category(category c) : val(c) {}
bool operator >= (const Category &c) { return val >= c.val; }
static const Category Pistol, MachineGun, Cannon;
};
const Category Category::Pistol(Category::pistol);
const Category Category::MachineGun(Category::machineGun);
const Category Category::Cannon(Category::cannon);
struct Killer {
Killer(Type t, Category c) : type(t), category(c) {}
Type type;
Category category;
};
int main(int argc, char *argv[])
{
// 使用类型包装后的 enum
Killer notCool(Type::General, Category::MachineGun);
// ......
if (notCool.type >= Type::General) {
std::cout << "It is not general" << std::endl;
}
// if (notCool.type >= Category::Pistol) { // error
// std::cout << "It is not a pistol" << std::endl;
// }
std::cout << std::is_pod::value << std::endl; // 0
std::cout << std::is_pod::value << std::endl; // 0
return 0;
}
缺点:
- 复杂
- POD 的 enum 被封装成 非 POD
- 大多数系统的ABI规定,传递参数的时候如果参数是一个结构体,就不能使用寄存器来传参。而整形可以使用寄存器传递。所以可能会有性能上的损失。
#include
enum C { C1 = 1, C2 = 2};
enum D { D1 = 1, D2 = 2, Dbig = 0xFFFFFFF0U };
enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFFFFLL };
int main(int argc, char *argv[])
{
std::cout << sizeof(C1) << std::endl; // 4
std::cout << Dbig << std::endl; // 4294967280
std::cout << sizeof(D1) << std::endl; // 4
std::cout << sizeof(Dbig) << std::endl; // 4
std::cout << Ebig << std::endl; // 68719476735
std::cout << sizeof(E1) << std::endl; // 8
return 0;
}
- 编译器会根据 enum 的数值,对 enum 数据长度进行扩展。
强类型枚举以及C++11对原有枚举类型的扩展
弱枚举类型的作用域,隐式转换,占用存储空间的不确定都是枚举类的缺点。
针对上述缺点,C++11 引入了强枚举类型。(strong-typed enum)
enum class Type { General, Light, Medium, Heavy };
- 强作用域:强类型枚举成员的名称不会被输出到其父作用域空间。
- 转换限制: 强类型枚举成员的值不可以与整型隐式的互相转换。
- 可以指定底层类型。默认类型是 int, 但也可以显式的指定类型。type 可以是除 wchar_t 以外的任何整型。
enum class Type : char { General, Light, Medium, Heavy };
#include
enum class Type { General, Light, Medium, Heavy };
enum class Category { General = 1, Pistol, MachinGun, Cannon };
int main(int argc, char *argv[])
{
Type t = Type::Light;
t = General; // error
if (t == Category::General) { // error,无法比较
// ...
}
if (t > Type::General) { // ok
// ...
}
if (t > 0) { // error, 不可以隐式转换
// ...
}
if ((int)t > 0) { // ok
// ...
}
std::cout << std::is_pod::value << std::endl; // 1
std::cout << std::is_pod::value << std::endl; // 1
return 0;
}
- 匿名 enum class
enum class { General, Light, Medium, Heavy } weapon;
int main()
{
weapon = General; // error
bool b = (weapon == weapon::General); // error
return 0;
}
因为是强作用域,所以 匿名 enum class 啥都做不了.