枚举和前置声明以及可能引起的问题

 在VC++ 2008下编译如下代码:

void func(enum EnumType type)
{
    
//......
}
int main()
{
    EnumType type = (EnumType)0;
    func(type);
    
return 0;
}

你会发现这段代码可以成功的编译并且运行,但是如果你足够细心,应该会发现EnumType并没有被定义,为什么使用没有定义的类型却能正常编译甚至运行呢?

原因有二: 

1. 函数中的参数声明enum EnumType type,不但被解析为一个参数声明,也被解析为对EnumType的一个前置声明。
其实不光对enum,对class也是一样,比如

void func(class Base b)
{
    
//......
}

也是没有问题的,这里在声明函数参数的同时,因为加了class这个关键词,也达到了前置声明calss Base的效果。

2. VC++的扩展对enum的前置声明的支持。
我们知道,C++标准其实是不支持enum的前置声明的,原因在于标准规定Enum的size是由其所容纳的值所确定的,所以在看到所有的值之前,编译器是无法确定其存储的。在这篇讨论中可以得到很多信息。而在VC++实现中,确定enum的size为4个字节,从而使Enum的前置声明成为可能 --- 不得不说,我认为这是VC++不同于标准,却又高于标准的实现。

所以以上代码其实是前置声明并使用了EnumType,因为并没有设计到EnumType的具体的枚举值,所以使用并没有任何问题 --- 毕竟,Enum是个4字节的整型数而已。

我们知道,在C中,当我们使用一个定义好的enum时(struct也一样),我们需要在定义的类型前加上前缀enum ,而不是直接使用该类型,比如前面的EnumType, 我们应该以enum EnumType的方式来使用,但在C++中,这个限制不再存在,但是这种用法被保留了下来。所以我们可以看到在不少地方大家还是会这么用。这就为引入问题提供了方便:

你以为你在使用你定义的那个enum,其实你只是在使用你自己前置声明的那个而已


 举个我在现实项目中遇到的问题,看如下代码:

 1 class Scope
 2 {
 3 public:
 4     enum EnumType {kType1, kType2};
 5 };
 6 
 7 class Base
 8 {
 9 public:
10     virtual void Func(enum Scope::EnumType type){}
11 };
12 
13 class Derived: public Base
14 {
15 public:
16     virtual void Func(enum EnumType type)
17     {
18         // Compiler can't catch the error even you compare 2 different enum types
19         if(type == Scope::kType1) return;
20     }
21 };
22 
23 int main()
24 {
25     Scope::EnumType type = Scope::kType1;
26     Base* pBase = new Derived();
27     pBase->Func(type);
       delete pBase;

28     return 0;
29}

 

这里试图在Derived中重载Base中的Func虚函数, 但是在声明参数的时候用了enum EnumType,而不是enum Scope::EnumType,而编译器不会报错。但事实上, 这里是直接前置声明了一个与Scope::EnumType截然不同的枚举类型EnumType,结果是Derived::Func永远也不会被调到,因为它根本就不是个虚函数Base::Func的重载。

了解了这些,我的对使用Enum的建议是:

 

  1. 在C++中使用enum时,最好直接用类型名,而不是像C中那样加个enum的前缀,主要是放了防止意外的前置声明。
  2. VC++中的enum的前置声明是个好东西,应该加以利用 ,但使用时要显示的来用。 

 

 

posted @ 2010-10-16 15:03  lzprgmr  阅读(5593)  评论(5编辑  收藏  举报

黄将军