这几天coding的时候看到很多enum关键字,翻看C++ Primer Plus之后还是感觉少了点关于这个关键字的介绍,因此就在CSDN找到2篇大佬写的文章,我看着挺全面的,现在就用本篇博客来记录自己学习enum关键字的心得体会吧。
下面将介绍enum关键字的定义,特点,以及C++11中引入的关于enum的内容
不管是c语言,还是c++语言, 其中都有enum关键字。这是这两种语言的基础知识中都会涉及到的点。
枚举 enum 的定义:
枚举 enum 是一个类型(class),可以保存一组由用户自定义的值。
enum 的具体定义格式为:enum <类型名> {<枚举常量表>};
关键字 enum —— 指明其后的标识符是一个枚举类型的名字。
枚举常量表 —— 由枚举常量构成。“枚举常量"或称"枚举成员”,是以标识符形式表示的整型量,表示枚举类型的取值。枚举常量表列出枚举类型的所有取值,各枚举常量之间以英文的逗号","间隔,且必须各不相同。
枚举类型有3种定义方式(我的理解):
①具体定义
②匿名定义
③与typedef结合使用定义
下面依次作介绍:
①枚举类型enum的具体定义方法如下:
//enum的具体定义方法
enum my_enum
{
my_enum1 = 0,
my_enum2,//注意:每一个枚举类型的常量以英文逗号","作分割
};
//这里定义了枚举类my_enum,其中的枚举常量有my_enum1(value 0),my_enum2(value 1)
enum week { Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat };
//这里定义了枚举类week,有7个枚举常量:
//Sun,Mon,Tue,Wed,Thu,Fri,Sat的值分别为7、1、2、3、4、5、6。
②枚举类型enum的匿名定义方式
enum { male, female };
//这里定义了一个匿名的枚举类,并有2个枚举常量:
//male(value 0),female(value 1)
③枚举类型enum与 typedef 的结合使用
typedef enum{men,women} human_type;
//定义了一个枚举类型,并有2个枚举常量:men(value 0),women(value 1)
//并把其枚举类型的名称typedef自定义为 human_type
human_type m = men;
cout << "m = " << m << endl;
运行结果:
①作用域:enum 关键字的作用域是全局的,如果在enum类型A中声明了一个枚举类型 my_enumya,则无法在enum类型B中声明同名的(也即名字也是my_enumya的)枚举类型常量了。
请看以下代码:
enum A { my_enumya = 1 };
enum B { my_enumya = 1 };//报错
//在枚举类B中也定义一个与枚举类A中同名的枚举常量my_enumya
将其放在一个void test(){}函数中,运行结果:
因此,我们可知道在一个函数内你定义了一个枚举类型,其中的枚举常量的名称绝对不可再次出现在该函数的另一个枚举类型的枚举常量当中,当然,改名称也绝不能出现在原枚举类型的其他枚举常量上。综上,枚举类型中的枚举常量的名称在一个函数内是唯一的!
请看以下代码:
void test()
{
enum A { my_enumya = 1 };
//enum A { my_enumya = 1 , my_enumya = 2};//错误!一个枚举类型中的枚举常量也不可以重名
//enum B { my_enumya = 1 };
//在枚举类B中也定义一个与枚举类A中同名的枚举常量my_enumya
//错误!冲定义了枚举常量,因为枚举常量的作用域就是全局的(一个函数内的全局)
//除非你在一个函数内定义这个枚举类型的枚举常量之后,在另外一个函数内再次定义同名的枚举常量,
//这样就是可行的
}
int main()
{
test();
enum C { my_enumya = 1 };//可以在另一个函数中去定义同名的枚举常量
system("pause");
return 0;
}
这样你的代码就没问题了!当然这种合法的同名枚举常量肯定是容易造成你的codes不易读,你应当适当避免这种做法。
②enum中枚举常量的取值特点:枚举常量代表该枚举类型的变量可能取得的值,编译系统为每个枚举常量指定一个整数值,默认状态下,这个整数就是所列举元素的序号,序号从0开始。可以在定义枚举类型时为部分或全部枚举常量指定整数值,在指定值之前的枚举常量仍按默认方式取值,而指定值之后的枚举常量按依次加1的原则取值。 各枚举常量的值可以重复。
请看以下代码:
enum color{red,blue=0,yellow=2,green, purple,white=0};
//定义了一个名为color的枚举类,其枚举常量:
//red(value 0),blue(value 0),yellow(value 2),green(value 3),purple(value 4),white(value 0)
cout<< "red(value "<
运行结果:(从下图的运行结果你就可以理解上述enum关键字的取值特点了)
注意这个坑:普通的enum枚举类型既不能是字符常量,也不能是整型常量
(当然,后面介绍的C+11引入的enum class就可以指定底层的底层的数据类型)
请看以下代码:
enum letter_set { 'f', 'a', 'n', 'f', 'a','n','y','a' }; //报错!枚举常量不能是字符常量
enum year_set { 1998, 1999, 2000, 2003, 2004, 2005 }; //报错!枚举常量不能是整型常量
③enum类型常量可隐式转换为其他类型(如整型)
请看以下代码:
enum D { mm = 110, };
int mm_int = mm;// enum -->(隐式转换) int
cout << "mm_int = " << mm_int << endl;
运行结果:
④enum无法指定底层使用的数据类型
也即:我们无法明确地知道,一个枚举类型,其所占用内存的字节数,这样在结构体中使用enum的时候就可能遇到麻烦, 特别是结构体需要内存对齐或者填充处理的时候问题就尤为突出了。(这个特点我当前还不理解,日后随着学习coding的深入再回看自己写的博客,以加深理解)
三、 enum关键字的相关操作
①枚举变量的使用 and ②枚举变量的赋值操作
请看以下代码:
enum my_enum
{
my_enum1 = 10,
my_enum2,
};
my_enum m1 = my_enum1;
//把枚举常量my_enum1的值赋值给枚举变量m1 值为10
my_enum m2 = my_enum2;
//把枚举常量my_enum2的值赋值给枚举变量m2 值为11
cout << "m1 = " << m1 << "\tm2 = " << m2 << endl;
运行结果:
③枚举类型运用比较运算符:==、<、>、<=、>=、!=
接着上述代码,请看以下代码:
if ( m1 == m2 )
cout << "m1 == m2 !" << endl;
else if( m1 > m2)
cout << "m1 > m2 !" << endl;
else if (m1 < m2)
cout << "m1 < m2 !" << endl;
m1 = 10,m2 = 11;
运行结果:
m1 = 1000,m2 = 101;
运行结果:
m1 = 112,m2 = 112;
运行结果:
四、C++ 11 中引入的 enum class 和 enum struct
①enum class(与enum struct是一样的)解决了传统 enum 关键字的作用域问题
(也即不允许重复定义同名的枚举常量这个问题)
请看以下代码:
enum class AA
{
my_sex = 0,
};
enum class BB
{
my_sex = 1,
};
enum CC
{
my_sex = 2,
};
//不同的enum class类中可以重复定义 同名的枚举常量
AA aa = AA::my_sex;
BB bb = BB::my_sex;
CC cc = CC::my_sex;
当然,此时,若你要输出对应的CC枚举类型的枚举变量的话,是没问题的
//没有用enum class类的话,就是普通的enum的话无需重载<<运算符
cout << "cc = " << cc << endl;
运行结果:
但是你想输出enum class AA和enum class BB对应的枚举变量的话,若不重载对应的<<运算符的话,是绝对不行的!当然,你当前真的没有办法去重载输出一个枚举变量的值,这在当前是不允许的!(我已经试过很多次了!)
请看下图:
你只能像这样重载<<运算符:
请看以下代码:
enum class AA
{
my_sex = 0,
my_sex2 = 2,
};
ostream& operator<< (ostream& out, const enum AA& aa)
{
switch (aa)
{
case AA::my_sex: out << "my_sex = 0" << endl; break;
case AA::my_sex2: out << "my_sex2 = 2" << endl; break;
}
return out;
}
int main(void)
{
AA aa = AA::my_sex;
cout << aa << endl;
system("pause");
return 0;
}
运行结果:
你只能在重载<<运算符的函数中去使用enum class中的枚举常量来do一些比较判断等的操作,
你决然没办法在重载<<运算符的函数中去直接输出对应枚举常量的值!
比如你将上述重载<<的函数改为:
ostream& operator<< (ostream& out, const enum AA& aa)
{
out << aa << endl;
return out;
}
运行结果将是(也必然是报错的!):
② enum class 可以指定底层数据类型(而普通的enum枚举类型是不能指定底层的数据类型的)
请看以下代码:
enum class MyEnumInterger :int//这里指定了 每一个枚举类型都是 int 型
{
num1 = 0,
num2 = 2,
num3 = 33,
};
enum class MyEnumDouble :unsigned char//这里指定了 每一个枚举类型都是 unsigned char 型
{
num1 = 0,
num2 = 2,
num3 = 33,
};
五、类中的枚举类型
当希望某些常量只能在类中有效。由于 #define 定义的宏常量是全局的,不能达到目的,于是我们可以采用 const 关键字来修饰数据成员。然而,const 数据成员只是在某个对象生存期内是常量,而对于整个类而言是可变的,因为类可以创建多个对象,不同的对象的 const 数据成员值是可以不一样的。
请看以下代码:
class Person
{
public:
Person(string name, int64_t number):m_Name(name), m_NumberOfSchool(number){}
void showInfo()
{
cout <<"姓名:"<< this->m_Name << endl;
cout <<"学号:" << this->m_NumberOfSchool << endl;
}
private:
const string m_Name;//维护每个Perosn对象中的姓名
const int64_t m_NumberOfSchool;//维护每个Perosn对象中的学号
//虽然类中的这些个成员属性是const的,但是对于每一个不同的对象而言
//其自身的这两个属性是可以不一样的!
};
int main()
{
Person p1("lzf", 201724104121),p2("lyf",201824104122);
Person p3(p1);
p1.showInfo();
p2.showInfo();
p3.showInfo();
system("pause");
return 0;
}
在C++ 11标准前,不能在类内声明中初始化 const 数据成员。但现在C++11标准早已普及,应当掌握下面这种用法。
也即:若要在类内建立恒定的常量可以使用枚举常量来实现。
请看以下代码:
class Person
{
public:
typedef enum
{
male = 0, female = 1,
}SexType;//定义了一个名为SexType的enum类型,包含2个枚举常量male(value 0),female(value 1)
Person(string name, SexType sex, int64_t number):m_Name(name), m_Sex(sex), m_NumberOfSchool(number){}
void showInfo()
{
cout << "姓名:" << this->m_Name <<"\t";
switch (this->m_Sex)
{
case 0:cout << "性别:male\t"; break;
case 1:cout << "性别:female\t"; break;
}
cout <<"学号:" << this->m_NumberOfSchool << endl;
}
private:
const string m_Name;//维护每个Perosn对象中的姓名
const SexType m_Sex;//维护每个Perosn对象中的性别
const int64_t m_NumberOfSchool;//维护每个Perosn对象中的学号
//虽然类中的这些个成员属性是const的,但是对于每一个不同的对象而言
//其自身的这两个属性是可以不一样的!
};
int main()
{
//这里使用static_cast<你需要强制转换过来的类型>(你想要转换的数据)来进行 int -->enum类型
Person p1("lzf",static_cast(0), 201724104121);
Person p2("lyf",static_cast(1), 201824104122);
Person p3(p1);
p1.showInfo();
p2.showInfo();
p3.showInfo();
system("pause");
return 0;
}
static_cast<>()的用法在这里看:static_cast的用法
小Tips:
1)枚举常量不会占用对象的存储空间,在编译时会被全部求值。
2)枚举常量的隐含数据类型是整型int,其类型的最大值是有限的(当然你可以设置枚举类型的取值范围,这个小知识我现在尚未理解,日后学习coding的过程中我再翻越回自己的学习笔记时,再加以补充纠正学习), 且枚举常量不能表示为浮点数(double or float)
参考:
C++基础 关键字:enum
c++ 11 enum的使用