enum class

强类型枚举

枚举:分门别类与数值的名字

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 啥都做不了.

你可能感兴趣的:(enum class)