枚举和前置声明以及可能引起的问题
在VC++ 2008下编译如下代码:
void func(enum EnumType type)
{
//......
}
int main()
{
EnumType type = (EnumType)0;
func(type);
return 0;
}
{
//......
}
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){}
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;
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的建议是:
- 在C++中使用enum时,最好直接用类型名,而不是像C中那样加个enum的前缀,主要是放了防止意外的前置声明。
- VC++中的enum的前置声明是个好东西,应该加以利用 ,但使用时要显示的来用。