深入理解C++枚举类型enum

参考:http://blog.csdn.net/weiwenhp/article/details/8476767

http://blog.csdn.net/mtfxabc/article/details/6492423

http://blog.csdn.net/lanxuezaipiao/article/details/41647333#comments

为啥需要枚举类型

编程语言中的所有特性都是为了满足某种需求,达到某个目的还出现.不会莫名其妙的出现在那.

枚举可以用来保存一组属性的值.enum的全称是enumeration意思是列举

看着这句话可能觉得太书面化了,不够通俗易懂.那举些通俗的例子说说.日常生活中我们特喜欢分类,比如读书时分啥数,理,化.当官的级别有啥省长,市长,县长.军队有军长,师长,团长.这样一组组的属性值就最适合用枚举类型来表示.当用一个软件时,有些页面会有很多单选按钮(radio button),这也特别适合用枚举来表示你举了哪一个.

 

光这样说你可能还不能体现枚举的好处.如果没枚举,表示一些组属性的值你只能用一组数字,或者一组字符串.

数字从字面上看不出任意意义,可读性非常差,所以很少用.那就假如要你通过比较字符串来做很多种类判断,比如 if(HisTitle == "stadholder")   else if(HistTile == "mayor") .如果让你敲个几十次你就知道是件多麻烦痛苦的事了,很多单词如果敲错一个意思就完全变了,这样出现bug了也不容易找到.老是复制粘贴也较麻烦.所以字符串是编辑麻烦,容易出错.如果要使用枚举就极大的方便我们敲代码,集成开发工具中的智能感应会给你提示,敲个点号就带出来了.而且枚举会做类型检查,不会像字符串那样只能靠你自己去对比.

 

枚举类型的内存分配问题

 

上面我们讲了如果没有枚举,一般会想到用数字或字符串表示某个类别.这样使用肯定不方便.也许你可能会想到用宏来表示.比如#define 市长 "mayor" 或者#define 市长 2

这自然是一个方法,但一来嘛在C++中是不太推荐用宏的,尽量少用.因为C++是强类型的语言,希望通过类型检查来降低程序中的很多错误,而宏只是在编译期前做简单替换,绕过了类型检查,失去了强类型系统的优势支撑. 二来嘛一组属性值都是相关联的信息,必须放到一起,放到一组.

 

关于常量的误解

枚举类型成员是常量

这句话怎么理解呢.也就是说enum  MyEnum{ one = 1 , two , three} ;

与 const int one = 1;  const int two = 2; const int three = 3; 差不多是一样的.

 

说到常量其实有个非常误导人的地方因为用宏#define 可以定义的我们说是常量,这里只涉及到简单的替换自然不可能存在内存分配问题.但是用const定义的也叫常量,而const定义常量貌似跟定义一般的变量只多个const关键字. 你可能会想当然认为常量都只是简单替换,所以不存在内存分配.那按这个逻辑,岂不是const定义的常量,枚举类型都没有内存分配?

实际上大部分时候确实是这样的.但并非总是如此,有些情况会需要分配内存的.

 

1.不需要分配内存的情况

如果定义常量const int one = 1;然后在其他地方只是把one作为右值赋值给其他变量那就不存在内存分配.但这里的常量跟#define定义的常量不同,宏定义的常量是编译前简单替换掉,而不需要做类型检查.而const定义的常量在编译时会帮类型检查,编译完之后再做替换.所以编译完之后就看不到const的信息了,转换成对应的值.const定义的信息只是保存在符号表中.

那同样,如果只是enum MyEnum{ one = 1 , two , three} ;这样定义一个枚举类型,然后也是简单的作右值赋值给其他变量.比如int num = MyEnum::one;那也只是保存信息在符号表中,编译后被替换掉了.

有人可能说如果用sizeof MyEnum测下会发现会是4(这是VS里面,不同的编译器可能不一样)于是认为不管是枚举里面有多少个元素内存分配都是4.实际上不是这意思,应该是定义一个MyEnum类型的枚举变量时会分配内存.这跟定义了一个类一样,你用sizeof去测一个为也会看到大小,但我们知道只有当类实例化之后才实际分配内存的.

 

2.需要分配内存的情况

1.)const int one;是类的成员变量 2. )extern const int one = 123;    3.)const int one = 1;    int* pConst = &one;

上面三种情况会需要分配内存.

而枚举类型,如果不是简单的去给其他变量赋值,而是去定义一个枚举类型变量.

比如MyEnum grade = MyEnum::one; //此时会分配4字节内存空间.(不过据说编译器会做优化,如果枚举类型所有值用两个字节表示就足够了,那实际分配的会就只会是两字节了.不一定就是默认的int类型的长度)

 

 

枚举类型具体用法

一般的用法是在全局域内定义一个枚举类型.比如

enum  MyEnum { one, two, three }

如果不显式指定,就会把第一个值默认赋值为0,然后递增1依次赋值.如果显式指定了某个值,则它下一个是它加1.

所以上面的例子中默认one = 0; two = 1; three= 2;

如果显式指定enum MyEnum { one, two = 3, three }

则one = 0; two = 3; three = 4;

 

定义一个枚举类型就是MyEnum grade = MyEnum::one;

 

类中使用枚举这是不太常用的用法.

在类中声明一个枚举后,定义枚举类型就可以省掉那个域作用前缀.比如MyEnum my = one; 在相同的作用域内也不能出现某个变量的名字和枚举中的元素名字相同,也就是不能出现其他变量名字是on,two, three

另外枚举还有一种少见的用法是

enum { one ,two ,three};  就是不指定一个名字,这样我们自然也没法去定义一些枚举类型了.此时就相当于const int one = 0;这样定义三个常量一样.

然后用的话就是int no = one;


enum的秘密<一>:enum是“用户自定义类型”

    呵呵,enum在实际中应用比较少,所以你会忽略它。但是,在这里,我告诉你,enum 和 struct、class一样,都是用户自定义类型。
    对呀!enum是用户自定义类型,他有数据成员,还有成员函数!
For example:
    enum e{a=1 , b=2 , c=4};
那么:
    001: enum e e1;        //enum e不是对象,它是类型,e1才是类型enum的对象!
    002: e e1;                //e是类型enum e的简写哦!

    003: e1 = 1;              //绝对的错误!int怎能赋值给一个用户自定义类型
    004: e1 = e();            //e()? 对呀,你没看错,默认构造函数
    005: e1 = e(1)           //e(int)? 嘿嘿,这才是从int构造enum e类型对象的构造函数
    006:e1 = a;              //哈哈,默认调用“拷贝构造函数”···虽然有点不完备

大胆的想:enum会不会有用户自定义成员函数呢?呵呵,我是没有实验出来。要不,你自己试试?
感想:C++虽然是初期的OO语言,但是类型化的概念已经深入到了语言本身。面对一门OO语言,你要必须知道一件事:一切都是对象,一切都有类型···

enum的秘密<二>:enum的“取值范围”和“内存分配”

先纠正一个常见错误,很多人认为enum是个离散量集合,太理想化了^_^,简单思考一下就破了,不多说,入正题:
如何确定一个enum的取值范围?
For example:
            enum e1{ a=2, b=4 };

首先找到其绝对值的最大值,但为了容易理解,我先不谈负数,也就是先找到其最大值,这里的最大值是4。
4 用二进制表示就是 100,也就是需要3bits才能最小的容纳下4这个值,而3bits所能表示的范围是 0-7,于是e1的取值范围为[0,7]。

现在来看看负数,
            enum e2{ a=-2, b=4 };
其中绝对值最大的是4,需要3bits才能容纳下,但因为可以取负值(而最大元素b=4不是负值),也就是说需要增加一个符号位,那么就需要4bits。
4bits的取值范围是 1000 - 0111(二进制表示),也就是 -8 到 7(十进制表示)。
enum e3{ a=-4, b=2 } 就只需要3bits,取值范围是[-4,3]。


简单的说就是找到最少的能容纳下所有的元素的位数
为什么要获取enum的取值范围?因为C++标准规定超出枚举类型表示范围的赋值结果是undefined的
也就是说 e2 x = (e2)6 是肯定正确的,而 e2 y = (e2)8 行为是未定义的。
undefined的含义我就不多说了,想怎么去歪解就怎么去歪解^_^

enum的内存分配呢?

比如 e2 需要3bits,那么C++规定e2的尺寸只要容得下3bits就行,到底是取1个byte,还是4个byte,还是...,那由编译器自己决定。但是,C++标准在这里有个限制:1<= sizeof(enmu)<=sizeof(int)。


类里的const和enum 
下面的写法有什么问题吗?

class bob {
const size = 100; // illegal
int array[size]; // illegal
}


结果当然是编译不通过。why?因为const在类对象里进行了存储空间分配,编译器不能知道const的内容是什么,所以不能把它用作编译期间的常量。这意味着对于类里的常数表达式来说,const就像它在C中一样没有作用。

在类里的const意思是“在这个特定对象的寿命期内,而不是对于整个类来说,这个值是不变的”。那么怎样建立一个可以用在常数表达式里的类常量呢? 
一个普通的办法是使用一个不带实例的无标记的enum。枚举的所有值必须在编译时建立,它对类来说是局部的,但常数表达式能得到它的值,这样,我们一般会看到:

 class bob {
    enum { size = 100 };  // legal
    int array[size];      // legal
}

使用enum是不会占用对象中的存储空间的,枚举常量在编译时被全部求值。我们也可以明确地建立枚举常量的值:enum { one=1,two=2,three};


猜你喜欢

转载自blog.csdn.net/jisuanji198509/article/details/80950832