手记

C++11 强枚举类型


C的时代

枚举类型

在代码中直接写出来的常量,我们称之为“魔数”。例如:

int week = 1;

你说这个week是星期一,也有人说这个week应该是星期日。这玩意太容易引起歧义,让人摸不着头脑,如同莫名其妙的魔术,所以我们称之为“魔数”。

问题的关键是我们应该给这个常量一个确切的名字:

int monday = 1;

这看起来好多了,不会引起歧义了,于是你愉快地继续写道:

int monday = 1;
int tuesday = 2;
int wednesday = 3;
int thursday = 4;
int friday = 5;
int saturday = 6;
int sunday = 7;

没错,你搞出来了一堆的全局变量。不过别担心,这在C语言中是常规操作,没人会说什么。

但是这里还有一个问题,我们从这些全局变量中完全看不出它们应该是一起的?现在它们就是一帮散兵游勇,而你迫切需要把它们组个朋友圈。于是,C语言说,好吧,那我们就创造一个枚举的概念吧:

enum Week
{
 Monday,
 Tuesday,
 Wednesday,
 Thursday,
 Friday,
 Saturday,
 Sunday,
};

现在枚举是一种类型了,尽管这个改进比直接用全局变量强不了多少,但无论如何,还是勉强可以用的:

 Week week = Monday;
 week = Tuesday;

整型提升

很快,新的问题又冒出来了,枚举该怎么打印呢?

 Week week = Monday;
 week = Tuesday;

    std::cout << week << Wednesday;

你用IDE尝试了一下,然后看到输出了一个1,一个2。“就这?我想输出它的名称啊,而不是一个数字。”,你有些无语。

C编译器面对你的质问有些羞愧:“我确实不知道该怎么输出枚举,实际上,为了输出这些数字,我已经不得不扩展了整型提升的规则。”

你有些惊讶:“”整型提升?就像下面这个?“

 char c = 'a';
 int cc = c;

C编译器涨红的脸变得更红了,“对,就是这个,你也知道,我会的不多。”

你心头一惊,有种不好的预感,“那岂不是说,这枚举还能运算?”

你飞快的敲下了下面的代码:

#include <iostream>

enum Week
{
 Monday,
 Tuesday,
 Wednesday,
 Thursday,
 Friday,
 Saturday,
 Sunday,
};

enum Season
{
 spring, // 春
 summer, // 夏
 autumn, // 秋   
 winter, // 冬
};

int main()
{
 Week week = Monday;
 week = Tuesday;

 int m = week + 1;
 bool is = week > spring;

    std::cout << week << Wednesday;
}

果然,这枚举居然会算术运算,还会和其它的枚举进行比较,这是什么狗屁的类型?一个怪物吗?说好的强类型语言呢?

总结

好吧,无论你喜欢不喜欢,C语言的枚举就是这个鬼样子。

优点:

  1. 是个类型,勉强能用

缺点:

  1. 作用域有问题,没有局部化,会污染当前的名称空间。

  2. 整型提升,确实不得不用,但是也真的不严谨啊

C++98的时代

早期的C++直接继承了C的全部遗产,所以C的枚举也同时继承过来了。

“真是丑陋的玩意。”,老B大叔嘟囔了一声,然后就走了,啥也没干。所以C++98的枚举就是C的枚举。

老B大叔是有这个底气跳过这个小问题的,因为他知道C++程序员自己肯定会解决这个问题的。“不就是一个设计实现一个类似于枚举的新类型嘛,这个太简单了。”。于是我们就看到了下面的代码:

#include "pch.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

#include <utility>

using namespace std::rel_ops;

class WeekType
{
public:
 enum Week
 {
  _Monday,
  _Tuesday,
  _Wednesday,
  _Thursday,
  _Friday,
  _Saturday,
  _Sunday,
 };

 Week _self;

public:
 WeekType(Week week)
  :_self(week)
 {
 }

 bool operator<(const WeekType& other) const
 {
  return _self < other._self;
 }

 bool operator>(const WeekType& other) const
 {
  return _self > other._self;
 }

 const static WeekType Monday;
 const static WeekType Tuesday;
 const static WeekType Wednesday;
 const static WeekType Thursday;
 const static WeekType Friday;
 const static WeekType Saturday;
 const static WeekType Sunday;
};

const WeekType WeekType::Monday(WeekType::_Monday);
const WeekType WeekType::Tuesday(WeekType::_Tuesday);
const WeekType WeekType::Wednesday(WeekType::_Wednesday);
const WeekType WeekType::Thursday(WeekType::_Thursday);
const WeekType WeekType::Friday(WeekType::_Friday);
const WeekType WeekType::Saturday(WeekType::_Saturday);
const WeekType WeekType::Sunday(WeekType::_Sunday);

namespace UnitTestHello
{
 TEST_CLASS(UnitTestEnum)
 {
 public:

  TEST_METHOD(TestWeekType)
  {
   WeekType week = WeekType::Monday;

   Assert::IsTrue(week < WeekType::Sunday);
   Assert::IsTrue(week <= WeekType::Sunday);
  }
 };
}

作用域问题搞定;整型提升的问题搞定。就这?分分钟的事情嘛。

在C++98的时代,关于枚举,C++程序员有两个选择:

  1. 如果追求效率,那就直接用C的枚举

  2. 如果追求严谨,那就自定义一个类

C++11的时代

转眼来到了2011年,那些追求严谨同时又被Java、C#等后起之秀惯坏了的C++程序员再也忍受不了为了个破枚举就要写这么多代码的现状了。他们强烈要求,改进C++的枚举语法。

“都21世纪了,再不变法,大C++就要亡了啊。”

新的枚举类型很快被敲定,发布。实际上,这几乎就是将C++自定义类的工作内置到语言而已。还是上面的例子,现在被简化为:

  enum class WeekClass
  {
   Monday,
   Tuesday,
   Wednesday,
   Thursday,
   Friday,
   Saturday,
   Sunday,
  };

  TEST_METHOD(TestWeekClass)
  {
   WeekClass week = WeekClass::Monday;

   Assert::IsTrue(week < WeekClass::Sunday);
   Assert::IsTrue(week <= WeekClass::Sunday);
  }

就这?多了一个class关键字而已。对,就这,现在它被称为“强枚举类型”。

“现在我们甚至可以指定枚举类型底层的数据类型”,新的C++编译器向你献宝似的说。

  enum class WeekByte : unsigned char
  {
   Monday,
   Tuesday,
   Wednesday,
   Thursday,
   Friday,
   Saturday,
   Sunday,
  };

  TEST_METHOD(TestWeekByte)
  {
   WeekByte week = WeekByte::Monday;

   Assert::AreEqual(size_t(1), sizeof(WeekByte));
   Assert::AreEqual(size_t(4), sizeof(Week));
  }

就这?还凑合吧。作为一位老C++程序员,这点小玩意还真是勾不起你的兴致。于是你懒洋洋的摆了摆手:“都散了吧,该干什么干什么去。”


0人推荐
随时随地看视频
慕课网APP