【C++基础语法 枚举】C/C++ 中enum枚举量的介绍:介绍enum枚举量在C/C中的作用和使用方法

简介: 【C++基础语法 枚举】C/C++ 中enum枚举量的介绍:介绍enum枚举量在C/C中的作用和使用方法

前言

众所周知,C/C++语言可以使用#define和const创建符号常量,而使用enum工具不仅能够创建符号常量,还能定义新的数据类型,但是必须按照一定的规则进行,下面我们一起看下enum的使用方法。


步骤(一)——枚举量的声明和定义

(1)首先,请看下面的语句:

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

这句话有两个作用:

第一:声明enumType为新的数据类型,称为枚举(enumeration);

第二:声明MondayTuesday等为符号常量,通常称之为枚举量,其值默认分别为0-6。(后面会介绍怎样显式的初始化枚举量的值)

(2)接着利用新的枚举类型enumType声明这种类型的变量:

enumType Weekday就像使用基本变量类型int声明变量一样,如 int a;也可以在定义枚举类型时定义枚举变量

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

然而与基本变量类型不同的地方是,在不进行强制转换的前提下,只能将定义的枚举量赋值给该种枚举的变量,如:Weekday = Monday;或者Weekday = Sunday;不能将其他值赋给枚举变量,如:Weekday = 10;这是不允许的,因为10不是枚举量。也就是说Weekday只能是定义的Monday-Sunday这些定义过的枚举量。然而这不是绝对的,第六条会讲到利用强制类型转换将其他类型值赋给枚举变量。

(3)上面讲不能将非枚举量赋给枚举变量,那么能不能将枚举量赋给非枚举变量呢?

如:int a=Monday;这是允许的,因为枚举量是符号常量,这里的赋值编译器会自动把枚举量转换为int类型。

(4)前面讲可以对枚举进行赋值运算,那枚举变量能不能进行算术运算呢?

Weekday++;Weekday = Monday + Tuesday;这是非法的,因为这些操作可能导致违反类型限制,比如:Weekday = Sunday;

Weekday++;Weekday首先被赋予枚举量中的最后一个值Sunday(值为6),再进行递增的话,Weekday增加到7,而对于enumType类型来说,7是无效的。

总结:对于枚举,只定义了赋值运算符,没有为枚举定义算术运算。

(5)不能对枚举量进行算术运算,那么枚举量能不能参与其他类型变量的运算呢?

int a;a = 1 + Monday;这是允许的,因为编译器会自动把枚举量转换为int类型。

(6)第二条讲:在不进行强制转换的前提下,只能将定义的枚举量赋值给该种枚举的变量,言下之意就是可以通过强制转换将其他类型值赋给枚举变量:

Weekday = enumType(2);等同于:Weekday = Wednesday;但是,如果试图将一个超出枚举取值范围的值通过强制转换赋给枚举变量,会出现什么结果?

Weekday = enumType(20);结果将是不确定的,这么做不会出错,但得不到想要的结果。


步骤(二)——自定义枚举量的值

(1)前面讲通过定义enum enumType {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};

枚举量Monday、Tuesday等的值默认分别为0-6,我们可以显式的设置枚举量的值:

enum enumType {Monday=1, Tuesday=2, Wednesday=3, Thursday=4, Friday=5, Saturday=6, Sunday=7};

指定的值必须是整数!

(2)也可以只显式的定义一部分枚举量的值:

enum enumType {Monday=1, Tuesday, Wednesday=1, Thursday, Friday, Saturday, Sunday};这样Monday、Wednesday均被定义为1,则Tuesday=2,Thursday、Friday、Saturday、Sunday的值默认分别为2、3、4、5.总结:未被初始化的枚举值的值默认将比其前面的枚举值大1。

(3)第二条还说明另外一个现象,就是枚举量的值可以相同。


步骤(三)——枚举的取值范围

前面讲到可以通过强制转换将其他类型值赋给枚举变量:Weekday = enumType(2);这是合法的;但是Weekday = enumType(20);是非法的。

这里涉及枚举取值范围的概念:枚举的上限是 大于最大枚举量的 最小的2的幂,减去1

枚举的下限有两种情况:

一、枚举量的最小值不小于0,则枚举下限取0;

二、枚举量的最小值小于0,则枚举下限是 小于最小枚举量的 最大的2的幂,加上1。

举例来讲:

假如定义 enum enumType1 { First=-5,Second=14,Third=10 };则枚举的上限是16-1=15(16大于最大枚举量14,且为2的幂); 枚举的下限是-8+1=-7(-8小于最小枚举量-5,且为2的幂);


步骤(四)——枚举应用

enum enumType{
Step0, Step1, Step2
}Step=Step0;
//注意这里在声明枚举的时候直接定义了枚举变量Step,并初始化为Step0
```c
switch (Step)
{
  case Step0:{...;break;}
  case Step1:{...;break;}
  case Step2:{...;break;}
  default:break;
}

另外枚举还有一种少见的用法是 enum { one ,two ,three}; 就是不指定一个名字,这样我们自然也没法去定义一些枚举类型了。此时就相当于static const int one = 0;这样定义三个常量一样。然后用的话就是int no = one。



强类型枚举

强类型枚举(Strongly-typed enums),号称枚举类型,是C++11中的新语法,用以解决传统C++枚举类型存在的缺陷。传统C++中枚举常量被暴漏在外层作用域中,这样若是同一作用域下有两个不同的枚举类型,但含有相同的枚举常量也是不可的,比如:

enum Side{Right,Left};
enum Thing{Wrong,Right};

这是不能一起用的。

另外一个缺陷是传统枚举值总是被隐式转换为整型,用户无法自定义类型。C++11中的强类型枚举解决了这些问题。

enum classenum在C++中都是用来定义枚举类型的,但是它们之间有一些重要的区别。

  1. 作用域enum class(也被称为强类型枚举)引入了自己的作用域。这意味着你不能直接使用枚举值,而必须通过枚举类型来访问它们。例如,如果你有一个enum class Color,你必须使用Color::Red来访问红色,而不能直接使用Red。而对于enum(也被称为普通枚举或非限定枚举),它的枚举值是在枚举类型的作用域之外可见的,所以你可以直接使用Red
  2. 类型安全enum class提供了更好的类型安全。例如,你不能将一个enum class的值赋给一个整数,也不能将一个整数赋给一个enum class的值,除非你使用静态类型转换。而对于enum,它的值可以隐式地转换为整数,也可以将整数隐式地转换为枚举值。
  3. 底层类型enum class允许你明确地指定底层类型。例如,你可以定义enum class Color : char,这样Color的底层类型就是char。而对于enum,如果你不指定底层类型,那么它的底层类型是int

因此,使用enum class,是因为它提供了更好的作用域控制和类型安全。这可以帮助防止一些常见的编程错误,例如无意识地将枚举值和整数混淆。


语法

强类型枚举使用enum class语法来声明,如下:

enum class Enumeration{
VAL1,
VAL2,
VAL3=100,
VAL4
};

这样,枚举类型时安全的,枚举值也不会被隐式转换为整数,无法和整数数值比较,比如(Enumeration::VAL4==10会触发编译错误)。

另外枚举类型所使用的类型默认为int类型,也可指定其他类型,比如:

enum calss Enum:unsigned int{VAL1,VAL2};
• 1

正如前面所说,强类型枚举能解决传统枚举不同枚举类下同枚举值名的问题,使用枚举类型的枚举名时,必须指明所属范围,比如:Enum::VAL1,而单独的VAL1则不再具有意义。

还有一点值得说明的是C++11中枚举类型的前置声明也是可行的,比如:

enum calss Enum;
enum class Enum1:unsigned int;

项目中的强类型枚举代码片段

1、图像处理

enum class Color{RED,BLUE,YELLOR,BLACK,WHITE};

2、交通灯

enum class TrafficLight{RED,YELLOR,GREEN};

强类型枚举值具有传统枚举的功能——命名枚举值,同时又具有类的特点——具有类域的成员和无法进行默认的类型转换。所以也称之为枚举类——enmu class

枚举类的底层数据必须是有符号或无符号整型,比如char unsigned int unsigned long,默认为int。

3、前置声明应用

enum class Color:char; //前置声明
void Foo(Color *p); //前置声明的使用
enum class Color:char{RED,GREEN,BLACK,WHITE}; //定义

枚举到字符串的相互映射

方式一:哈希表

你可以创建一个从枚举到字符串的映射,以及一个从字符串到枚举的映射。这样,你可以在需要的时候使用字符串,但在查找和比较的时候使用枚举,这可以提高性能。

以下是一个简单的示例:

enum class SubClassType {
    SubClass1,
    SubClass2,
    // 其他子类
};
std::unordered_map<SubClassType, std::string> enumToString = {
    {SubClassType::SubClass1, "SubClass1"},
    {SubClassType::SubClass2, "SubClass2"},
    // 其他子类
};
std::unordered_map<std::string, SubClassType> stringToEnum = {
    {"SubClass1", SubClassType::SubClass1},
    {"SubClass2", SubClassType::SubClass2},
    // 其他子类
};
// 使用字符串查找枚举
SubClassType type = stringToEnum["SubClass1"];
// 使用枚举查找字符串
std::string name = enumToString[SubClassType::SubClass1];

在这个示例中,enumToString是一个从枚举到字符串的映射,stringToEnum是一个从字符串到枚举的映射。你可以使用这两个映射在字符串和枚举之间进行转换。

这样,你可以在需要的时候使用字符串(例如,从配置文件中读取子类的名称),但在查找和比较的时候使用枚举(例如,查找子类的实例)。这可以提高性能,因为枚举的比较和哈希计算通常比字符串的比较和哈希计算更快。

方式二:编译器期处理

在C++中,std::unordered_map的初始化并不能在编译期完成,因为它涉及到动态内存分配和哈希函数的计算,这些都是在运行期进行的。即使你使用constexprconst,也不能改变这一点。

然而,如果你的枚举值和字符串之间的映射关系是固定的,你可以使用一个函数来在编译期生成对应的字符串或枚举值。这样的函数需要使用constexpr来保证它可以在编译期运行。

以下是一个简单的示例:

enum class SubClassType {
    SubClass1,
    SubClass2,
    // 其他子类
};
constexpr const char* EnumToString(SubClassType type) {
    switch (type) {
        case SubClassType::SubClass1: return "SubClass1";
        case SubClassType::SubClass2: return "SubClass2";
        // 其他子类
    }
    return nullptr;
}
constexpr SubClassType StringToEnum(const char* str) {
    if (strcmp(str, "SubClass1") == 0) return SubClassType::SubClass1;
    if (strcmp(str, "SubClass2") == 0) return SubClassType::SubClass2;
    // 其他子类
    return static_cast<SubClassType>(-1);
}

在这个示例中,EnumToString函数和StringToEnum函数都是constexpr函数,这意味着它们可以在编译期运行。你可以使用这两个函数在字符串和枚举之间进行转换。

请注意,这个方法只适用于枚举值和字符串之间的映射关系是固定的情况。如果映射关系是动态的,或者需要从外部输入(例如,从配置文件中读取),那么你仍然需要使用std::unordered_map或其他运行期的数据结构。

结语

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。

目录
相关文章
|
3月前
|
编译器 C语言 C++
☺初识c++(语法篇)☺
☺初识c++(语法篇)☺
|
8月前
|
Java C# C++
C++ 11新特性之语法甜点1
C++ 11新特性之语法甜点1
63 4
|
8月前
|
编译器 C++ 容器
C++ 11新特性之语法甜点2
C++ 11新特性之语法甜点2
66 1
|
8月前
|
存储 算法 编译器
C++ 11新特性之语法甜点4
C++ 11新特性之语法甜点4
61 0
|
8月前
|
安全 C++ 容器
C++ 11新特性之语法甜点3
C++ 11新特性之语法甜点3
69 0
|
9月前
|
编译器 C++ 容器
C++语言的基本语法
想掌握一门编程语言,第一步就是需要熟悉基本的环境,然后就是最重要的语法知识。 C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。 类 - 类可以定义为描述对象行为/状态的模板/蓝图。 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。 完整关键字
190 2
|
10月前
|
Java 编译器 程序员
C++中的语法知识虚继承和虚基类
**C++中的多继承可能导致命名冲突和数据冗余,尤其在菱形继承中。为解决这一问题,C++引入了虚继承(virtual inheritance),确保派生类只保留虚基类的一份实例,消除二义性。虚继承通过`virtual`关键字指定,允许明确访问特定路径上的成员,如`B::m_a`或`C::m_a`。这样,即使基类在继承链中多次出现,也只有一份成员副本,简化了内存布局并避免冲突。虚继承应在需要时提前在继承声明中指定,影响到从虚基类派生的所有后代类。**
150 7
|
10月前
|
编译器 C++ 开发者
C++一分钟之-属性(attributes)与属性语法
【7月更文挑战第3天】C++的属性(attributes)自C++11起允许附加编译器指令,如`[[nodiscard]]`和`[[maybe_unused]]`,影响优化和警告。注意属性放置、兼容性和适度使用,以确保代码清晰和可移植。示例展示了如何使用属性来提示编译器处理返回值和未使用变量,以及利用编译器扩展进行自动清理。属性是提升代码质量的工具,但应谨慎使用。
282 13
|
11月前
|
编译器 程序员 C++
C++一分钟之-属性(attributed)与属性语法
【6月更文挑战第28天】C++的属性为代码添加元数据,帮助编译器理解意图。C++11引入属性语法`[[attribute]]`,但支持取决于编译器。常见属性如`nodiscard`提示检查返回值,`maybe_unused`防止未使用警告。问题包括兼容性、过度依赖和误用。使用属性时需谨慎,确保团队共识,适时更新以适应C++新特性。通过示例展示了`nodiscard`和`likely/unlikely`的用法,强调正确使用属性能提升代码质量和性能。
200 13
|
9月前
|
安全 C++ Windows
Windows下C++使用gRPC(Qt和VS,含文件包和使用方法)
Windows下C++使用gRPC(Qt和VS,含文件包和使用方法)

热门文章

最新文章