C++枚举类型

枚举类型

枚举类型使我们可以将一组整型常量组织在一起。

和类一样,每个枚举类型定义了一种新的类型。

枚举属于字面值常量类型。

C++包含两种枚举:限定作用域的和不限定作用域的。

限定作用域的枚举类型

C++11新标准引入了限定作用域的枚举类型。

定义限定作用域的枚举类型的一般形式是:首先是关键字enum class(或者等价地使用 enum struct),随后是枚举类型名字以及用花括号括起来的以逗号分隔的枚举成员列表,最后是一个分号:

enum class open_modes (input, output, append);


我们定义了一个名为open_modes的枚举类型,它包含三个枚举成员:input、output和append。

不限定作用域的枚举类型

定义不限定作用域的枚举类型时省略掉关键字 class(或struct),枚举类型的名字是可选的:

enum color(red,yellow, green); // 不限定作用域的枚举类型

//未命名的、不限定作用域的枚举类型
enum(floatPrec=6, doublePrec = 10, double_doublePrec = 10};

如果enum是未命名的,则我们只能在定义该enum时定义它的对象。和类的定义类似,我们需要在 enum 定义的右侧花括号和最后的分号之间提供逗号分隔的声明列表

枚举成员

  1. 在限定作用域的枚举类型中,枚举成员的名字遵循常规的作用域准则,并且在枚举类型的作用域外是不可访问的。
  2. 与之相反,在不限定作用域的枚举类型中,枚举成员的作用域与枚举类型本身的作用域相同:

我们来看看容易用错的几个点子

易错点1:重复定义枚举成员

不限定作用域的枚举类型

enum A { a, b, c };
enum B { a, b, c };//重复定义枚举成员

 

这是因为在不限定作用域的枚举类型中,枚举成员的作用域与枚举类型本身的作用域相同

限定作用域的枚举类型

相反,在限定作用域的枚举类型中就不会出现这种问题

	enum class A { a, b, c };
	enum class B { a, b, c };//没有问题

这是因为枚举成员的作用域仅限于其对应的枚举类型,对外界是不可见的

所以我们可以混用不限定作用域的枚举类型和限定作用域的枚举类型

enum  A { a, b, c };
enum class B { a, b, c };//没有问题

易错点2:给枚举类型赋值

限定作用域的枚举类型

enum class B { a, b, c };
B b1 = a;//会报错
B b2 = B::a;//没有问题

我们不能在枚举类型定义外直接使用限定作用域的枚举类型的枚举成员,必须通过::来显式访问 

不限定作用域的枚举类型

enum A{a,b,c};
A a1 = a;//正确
A a2 = A::a;//正确

我们可以通过两种方式来使用不限定作用域的枚举类型的枚举成员。 

混用两种枚举类型

enum  A { a, b, c };
enum class B { a, b, c };//没有问题

A a1 = a;//没有问题,因为
B b1 = a;//有问题,因为B类型的b成员的作用域被限定在枚举类型中,
//所以此时赋给b1的是A::a,而不是B::a

A a2 = A::a;//没有问题
B b2 = B::a;//没有问题

就自己看吧

枚举成员的值

默认情况下,枚举值从0开始,依次加1。

enum A{a,b,c};
//默认a=0,b=1,c=2

不过我们也能为一个或几个枚举成员指定专门的值:

enum class_intTypes{
charTyp = 8, shortTyp = 16, intTyp = 16, 
longTyp = 32, long_longTyp = 64
}

由枚举成员 intTyp和shortTyp可知,枚举值不一定唯一。

如果我们没有显式地提供初始值,则当前枚举成员的值等于之前枚举成员的值加1。

enum A{a=9,b=3,c};//默认c的值是它前面的那个成员的值加一,也就是4

枚举成员是const

枚举成员是const,因此在初始化枚举成员时提供的初始值必须是常量表达式。

也就是说,每个枚举成员本身就是一条常量表达式,我们可以在任何需要常量表达式的地方使用枚举成员。

例如,我们可以定义枚举类型的constexpr变量:

enum A{a,b,c};
constexpr A a3= A::a;


类似的,我们也可以将一个enum作为switch语句的表达式,而将枚举值作为case标签

enum A{a,b,c};
 A a3= A::a;
 switch (a3)
 {
 case a:cout << "a" << endl; break;
 case b:cout << "b" << endl; break;
 case c:cout << "c" << endl; break;
}

出于同样的原因,我们还能将枚举类型作为一个非类型模板形参使用;

enum Color {
    RED,
    GREEN,
    BLUE
};

template<Color color>
class MyClass {
public:
    void printColor() {
        switch (color) {
            case RED:
                std::cout << "Red" << std::endl;
                break;
            case GREEN:
                std::cout << "Green" << std::endl;
                break;
            case BLUE:
                std::cout << "Blue" << std::endl;
                break;
        }
    }
};

int main() {
    MyClass<RED> redObj;
    redObj.printColor();  // 输出 "Red"

    MyClass<GREEN> greenObj;
    greenObj.printColor();  // 输出 "Green"

    return 0;
}

或者在类的定义中初始化枚举类型的静态数据成员。

class C
{
	enum  A { a, b, c };
	static int d[a] ;
	static const int e = b;
};

和类一样,枚举也定义新的类型

只要enum有名字,我们就能定义并初始化该类型的成员。

要想初始化enum对象或者为enum对象赋值,必须使用该类型的一个枚举成员或者该类型的另一个对象,

enum  A{a,b,c};
A a1 = A::a;//正确

一个不限定作用城的枚举类型的对象或枚举成员自动地转换成整型,限定作用域的枚举类型不会进行隐式转换

因此,我们可以在任何需要整型值的地方使用它们:

enum  A{a,b,c};
enum class B{d,e};
int a1 = a;//可以
int b1 = B::d;//不可以,限定作用域的枚举类型不会进行隐式转换

指定enum的大小

尽管每个enum都定义了唯一的类型,但实际上enum是由某种整数类型表示的。

在C++11新标准中,我们可以在enum的名字后加上冒号以及我们想在该enum中使用的类型:

enum intValues : unsigned long long {

charTyp = 255, shortTyp = 65535, intTyp = 65535, 
longTyp = 4294967295UL,
long_longTyp = 18446744073709551615ULL
};


如果我们没有指定enum的潜在类型,则默认情况下限定作用域的enum成员类型是int。

对于不限定作用域的枚举类型来说,其枚举成员不存在默认类型,我们只知道成员的潜在类型足够大,肯定能够容纳枚举值。

如果我们指定了枚举成员的潜在类型(包括对限定作用域的enum的隐式指定),则一旦某个枚举成员的值超出了该类型所能容纳的范围,将引发程序错误。

指定enum潜在类型的能力使得我们可以控制不同实现环境中使用的类型,我们将可以确保在一种实现环境中编译通过的程序所生成的代码与其他实现环境中生成的代码一致。

枚举类型的前置声明

在C++11 新标准中,我们可以提前声明 enum。

enum的前置声明(无论隐式地还是显示地)必须指定其成员的大小

//不限定作用域的枚举类型intValues的前置声明
enum A : unsigned long long;// 不限定作用域的,必须指定成员类型

enum class B; //限定作用域的枚举类型可以使用默认成员类型int
  1. 因为不限定作用域的enum未指定成员的默认大小,因此每个声明必须指定成员的大小,这里被指明为unsigned long long
  2. 对于限定作用域的enum来说,我们可以不指定其成员的大小,这个值被隐式地定义成int。

和其他声明语句一样,enum的声明和定义必须匹配,这意味着在该enum的所有声明和定义中成员的大小必须一致。

enum A : unsigned long long;

enum  A:long {a,b,c};//这是错误的


而且,我们不能在同一个上下文中先声明一个不限定作用域的enum名字,然后再声明一个同名的限定作用域的enum:
 

enum class intValues;// 错误:所有的声明和定义必须对该enum是限定作用域的还是不限定作用域的保持一致
enum intValues;// 错误:intValues已经被声明成限定作用域的

 enum intValues : long; //错误:intValues 已经被声明成int

形参匹配与枚举类型

要想初始化一个enum对象,必须使用该enum类型的另一个对象或者它的一个枚举
成员。

因此,即使某个整型值恰好与枚举成员的值相等,它也不能作为函数的enum实参使用:

//不限定作用域的枚举类型,潜在类型因机器而异
enum Tokens {INLINE = 128, VIRTUAL = 129};
void ff(Tokens);
void ff(int);

int main() {
Tokens curTok = INLINE;
ff(128); // 精确匹配 ff(int)
ff(INLINE) ;// 精确匹配 ff(Tokens)
ff(curTok); //精确匹配 ff(Tokens)
return 0;


尽管我们不能直接将整型值传给enum形参,但是可以将一个不限定作用域的枚举类型的对象或枚举成员传给整型形参。

此时,enum的值提升成int或更大的整型,实际提升的结果由枚举类型的潜在类型决定:

void newf (unsigned char);
void newf(int);
unsigned char uc = VIRTUAL;
newf(VIRTUAL); // 调用 newf(int)
newf(uc); // 调用 newf (unsigned char)


枚举类型 Tokens只有两个枚举成员,其中较大的那个值是129。该枚举类型可以用unsigned char来表示,因此很多编译器使用unsigned char作为Tokens的潜在类型。

不管Tokens的潜在类型到底是什么,它的对象和枚举成员都提升成int

尤其是,枚举成员永远不会提升成unsigned char,即使枚举值可以用unsigned char存储是如此。
 

  • 56
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质?你是否想成为一名资深开发人员,想开发别人做不了的高性能程序?你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹? 那么C++就是你个人能力提升,职业之路进阶的不二之选。【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署;2.吊打一切关于C++的笔试面试题;3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块基础篇本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。进阶篇本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。提升篇:本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值