原地址:http://www.cppblog.com/chemz/archive/2007/06/05/25578.html

至从C语言开始enum类型就被作为用户自定义分类有限集合常量的方法被引入到了语言
当中,而且一度成为C++中定义编译期常量的唯一方法(后来在类中引入了静态整型常量)。
    根据上面对enum类型的描述,到底enum所定义出来的类型是一个什么样的类型呢?作为
一个用户自定义的类型其所占用的内存空间是多少呢?使用enum类型是否真的能够起到有限
集合常量的边界约束呢?大家可能都知道enum类型和int类型具有隐示(自动)转换的规则,
那么是否真的在任何地方都可以使用enum类型的变量来代替int类型的变量呢?下面会逐一
回答这些问题。
    1. 到底enum所定义出来的类型是一个什么样的类型呢?
       在C++中大家都知道仅仅有两种大的类型分类:POD类型和类类型(不清楚的可以参
       见我的其他文章)。enum所定义的类型其实属于POD类型,也就是说它会参与到POD
       类型的隐示转换规则当中去,所以才会出现enum类型与int类型之间的隐示转换现象。
       那么也就是说enum所定义的类型不具备名字空间限定能力(因为不属于类类型),
       其所定义的常量子具备和enum类型所在名字空间相同的可见性,由于自身没有名字
       限定能力,所以会出现名字冲突现象。如:
           struct CEType
           {
               enum EType1 { e1, e2 };
               enum EType2 { e1, e2 };
           };
       上面的例子会出现e1、e2名字冲突编译时错误,原因就在于枚举子(e1、e2)是
       CEType名字空间中的名字,同样在引用该CEType中的枚举子时必须采用CEType::e1
       这样的方式进行,而不是CEType::EType1::e1来进行引用。
    2. 作为一个用户自定义的类型其所占用的内存空间是多少呢?
       该问题就是sizeof( EType1 )等于多少的问题,是不是每一个用户自定义的枚举类
       型都具有相同的尺寸呢?在大多数的32位编译器下(如:VC++、gcc等)一个枚举类
       型的尺寸其实就是一个sizeof( int )的大小,难道枚举类型的尺寸真的就应该是int
       类型的尺寸吗?其实不是这样的,在C++标准文档(ISO14882)中并没有这样来定义,
       标准中是这样说明的:“枚举类型的尺寸是以能够容纳最大枚举子的值的整数的尺寸”,
       同时标准中也说名了:“枚举类型中的枚举子的值必须要能够用一个int类型表述”,
       也就是说,枚举类型的尺寸不能够超过int类型的尺寸,但是是不是必须和int类型
       具有相同的尺寸呢?上面的标准已经说得很清楚了,只要能够容纳最大的枚举子的
       值的整数就可以了,那么就是说可以是char、short和int。例如:
           enum EType1 { e1 = CHAR_MAX };
           enum EType2 { e2 = SHRT_MAX };
           enum EType3 { e3 = INT_MAX  };
       上面的三个枚举类型分别可以用char、short、int的内存空间进行表示,也就是:
           sizeof( EType1 ) == sizeof( char  );
           sizeof( EType2 ) == sizeof( short );
           sizeof( EType3 ) == sizeof( int   );
       那为什么在32位的编译器下都会将上面三个枚举类型的尺寸编译成int类型的尺寸呢?
       主要是从32位数据内存对其方面的要求进行考虑的,在某些计算机硬件环境下具有对
       齐的强制性要求(如:sun SPARC),有些则是因为采用一个完整的32位字长CPU处理
       效率非常高的原因(如:IA32)。所以不可以简单的假设枚举类型的尺寸就是int类
       型的尺寸,说不定会遇到一个编译器为了节约内存而采用上面的处理策略。
    3. 使用enum类型是否真的能够起到有限集合常量的边界约束呢?
       首先看一下下面这个例子:

enum EType { e1 = , e2 };
void func1( EType e )
{
if ( e == e1 )
{
// do something
}
// do something because e != e1 must e == e2
}
void func2( EType e )
{
if ( e == e1 )
{
// do something
}
else if ( e == e2 )
{
// do something
}
} func1( static_cast<EType>( ) );
func2( static_cast<EType>( - ) );

上面的代码应该很清楚的说明了这样一种异常的情况了,在使用一个操出范围的整
       型值调用func1函数时会导致函数采取不该采取的行为,而第二个函数可能会好一些
       他仅仅是忽略了超出范围的值。这就说明枚举所定义的类型并不是一个真正强类型
       的有限常量集合,这样一种条件下和将上述的两个函数参数声明成为整数类型没有
       任何差异。所以以后要注意标准定义中枚举类型的陷阱。(其实只有类类型才是真
       正的强类型)
       
    4. 是否真的在任何地方都可以使用enum类型的变量来代替int类型的变量呢?
       通过上面的讨论,其实枚举类型的变量和整型变量具有了太多的一致性和可互换性,
       那么是不是在每一个可以使用int类型的地方都可以很好的用枚举类型来替代呢?
       其实也不是这样的,毕竟枚举类型是一个在编译时可区分的类型,同时第2点的分析
       枚举类型不一定和int类型具有相同的尺寸,这两个差异就决定了在某些场合是不可
       以使用枚举类型来代替int类型的。如:
           第一种情况:
               enum EType { e1 = 0, e2, e3 };
               EType val;
               std::cin >> val;
           第二种情况:
               enum EType { e1 = 0, e2, e3 };
               EType val;
               std::scanf( "%d", &val );
       上面的两种情况看是基本上属于同一种类型的问题,其实不然。第一种情况会导致
       编译时错误,会因为std::cin没有定义对应的枚举类型的重载>>运算符而出错,这
       就说明枚举类型是一种独立和鉴别的类型;而第二种情况不会有任何编译时问题,
       但是可能会导致scanf函数栈被破坏而使得程序运行非法,为什么会这样呢?上面
       已经分析过了枚举类型变量的尺寸不一定和int类型相同,这样一来我们采用%d就
       是说将枚举类型变量val当作4字节的int变量来看待并进行参数压栈,而在某些编
       译器下sizeof( val )等于1字节,这样scanf函数就会将val变量地址中的后续的三
       字节地址也压入栈中,并对其进行赋值,也许val变量后续的三个字节的地址没有
       特殊含义可以被改写(比如是字节对齐的空地址空间),可能会认为他不会出现错
       误,其实不然,在scanf函数调用结束后会进行栈清理,这样一来会导致scanf函数
       清理了过多的地址空间,从而破坏了外围函数的栈指针的指向,从而必然会导致程
       序运行时错误。

由上面的说明枚举类型有那么多的缺点,那我们怎样才能够有一个类型安全的枚举类型
呢?其实可以采用类类型来模拟枚举类型的有限常量集合的概念,同时得到类型安全的好处,

enum类型的本质(转)的更多相关文章

  1. EffectiveJava(30) -- 全面解析enum类型

    --在大多数项目中,我们会经常使用int类型来声明final类型的常量,它在不考虑安全的情况下确实能满足我们绝大多数的需求.但是在JDK1.5版本发布之后,声明一组固定的常量组成合法值的类型就建议使用 ...

  2. set和enum类型的用法和区别

    mysql中的set和enum类型的用法和区别 mysql中的enum和set其实都是string类型的而且只能在指定的集合里取值, 不同的是set可以取多个值,enum只能取一个值.   1 2 3 ...

  3. MYSQL中 ENUM 类型

    MYSQL中 ENUM 类型的详细解释 ENUM类型 ENUM 是一个字符串对象,其值通常选自一个允许值列表中,该列表在表创建时的列规格说明中被明确地列举. 在下列某些情况下,值也可以是空串(&quo ...

  4. C#中enum类型

    最近碰到了枚举类型,就顺便整理下. 枚举的基类Enum,可以是除 Char 外的任何整型.不做显示声明的话,默认是整形(Int32). 声明一个Enum类型: /// <summary> ...

  5. C#遍历enum类型

    对于enum类型: 使用foreach遍历enum类型的元素并填充combox foreach ( HatchStyle hs1 in Enum.GetValues(typeof(HatchStyle ...

  6. Java中Enum类型的序列化(转)

    在Java中,对Enum类型的序列化与其他对象类型的序列化有所不同,今天就来看看到底有什么不同.下面先来看下在Java中,我们定义的Enum在被编译之后是长成什么样子的. Java代码: Java代码 ...

  7. Java 语言中 Enum 类型的使用介绍

    Enum 类型的介绍 枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中.而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常 ...

  8. java中enum类型的使用

    java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java 中定义的常量值不采用enmu 枚举类型,而采用public final static 类型来定义呢?以前我 ...

  9. C#基础知识回顾--C#遍历enum类型、获取enum项个数

    C#遍历enum类型 对于enum类型: 使用foreach遍历enum类型的元素并填充combox foreach ( HatchStyle hs1 in Enum.GetValues(typeof ...

随机推荐

  1. 我用Cocos2d-x模拟《Love Live!学院偶像祭》的Live场景(三)

    [前言和思路整理] 千呼万唤Shi出来啊(好像也没人呼唤),最近公司项目紧,闲暇时间少更得慢,请见谅. 上一章我们分析并实现了打击物件类BeatObject,和它的父节点BeatObjectColum ...

  2. 天兔(Lepus)监控系统快速安装部署

    Lepus安装需要Lamp环境,Lamp环境的安装个人认为比较费劲,XAMPP的一键部署LAMP环境省心省力, lepus官网手册也建议采用XAMPP的方式安装,lepus也是在XAMPP上进行研发的 ...

  3. WPF dataGrid中的check的改变事件

    关于datagrid信息: <DataGridTemplateColumn Header="备注"> <DataGridTemplateColumn.CellTe ...

  4. MySQL数据类型--日期时间

    一.博客前言 自接触学习MySQL已有一段时间了,对于MySQL的基础知识还是略懂略懂的.在这一路学习过来,每次不管看书还是网上看的资料,对于MySQL数据类型中的时间日期类型总是一扫而过,不曾停下来 ...

  5. VisualGDB Makefiles

    以下内容是VisualGDB官网对VisualGDB编译时获取相关编译信息的说明: When you create a new project using VisualGDB, it will gen ...

  6. redis使用Lua脚本

    最近在看<Redis入门指南>第二版,感觉收获挺大,推荐大家有时间看一看.其中有一章讲Lua脚本,感觉挺实用,把总结整理一下. Redis在2.6中推出了脚本功能,允许开发者使用Lua语言 ...

  7. POJ2187(旋转卡壳)

    Beauty Contest Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 35459   Accepted: 10978 ...

  8. 一个web应用的诞生--美化一下

    经过上一章的内容,其实就页面层来说已结可以很轻松的实现功能了,但是很明显美观上还有很大的欠缺,现在有一些很好的前端css框架,如AmazeUI,腾讯的WeUI等等,这里推荐一个和flask集成很好的b ...

  9. Mac和Linux系统的:Arp欺骗源码

    linux系统, 简化版的ARP欺骗工具 精简版, 没有很多代码, 只要把准备好的数据, 发送给到网卡接口, 利用这个工具, 可以让局域网内的一台计算机暂时掉线: #include <stdio ...

  10. 改变radio默认样式

    改变radio默认样式,代码如下: <!doctype html> <html> <head> <meta charset="UTF-8" ...