跟我学C++中级篇—枚举的作用域分析

一、枚举体

enum,枚举体。无论是在C或C++中,都是很常见的。不过对于枚举体,很多开发者对其的了解并不全面,特别是在C++11中又引入了枚举类。这就让C中的枚举与C++11中的枚举类有了很大的不同。本文尝试着从作用域的角度对枚举和枚举类进行分析和说明。

二、枚举体作用域

作用域,开发者应该不陌生。所谓作用域,其实就是对象可以被访问的范围。而作用域往往和生命周期无法割裂开来。作用域和生命周期控制着对象(变量、参数、函数等)可以正常运行的限定条件。从而涉及到代码安全、指针风险等代码的基础应用。
可以从两个方面来对枚举的作用域进行说明即C类型的枚举和C++11的枚举类。

  1. 枚举类
    在C++11中,推出了枚举类。可以这样认为,枚举类就是解决普通枚举无法安全控制作用域而产生的。它有着典型作用域的管理的能力。在这个角度上,和普通的类没有什么区别。
  2. C类型的枚举
    C++的特点决定了,它既要兼容C又要有自己的独到的一面。在明白了C++11后的枚举类作用域处理后,其实重点就是C类型的枚举类了。这种枚举它是无作用域限制的。但这不代表它没有作用域,或者说它存在一些特别的作用域的情况。
    下面将对C类型的枚举的作用域进行详细的分析说明。

三、C枚举作用域分析

C类型的枚举,简称为枚举。枚举本身和宏有些类似,它直接暴露在外围限定作用域内(即相同的作用域限定符内),这样,如果不同的枚举在相同的作用域范围内或可见空间内,如果声明相同,则会产生命名冲突。看下面的例子:

enum DATA { D1, D2, D3 };//使用emnu class则不会出现这种问题
enum COLOR { D1, D2, D3 };

上面的代码编译器就会报一个重定义的冲突问题。
而在实际的应用中还有一种现象,也可能让不少的开发者忽略或者说不重视,看下面的代码:

//.h
#ifndef ENUMDEMO_H
#define ENUMDEMO_H

enum DATA { D_IN, D_OUT, D_BOTH };
//enum DATA_SIGN { D_1, D_2, D_BIG };
class EnumDemo {
public:
  enum DATA_SIGN { D_LITTLE, D_SMALL, D_BIG };

public:
  EnumDemo();

private:
  DATA initData(DATA_SIGN ds);
  // DATA_SIGN setData(DATA_SIGN ds);
  ::DATA_SIGN setData(DATA_SIGN ds);
};

#endif // ENUMDEMO_H
//.cpp
#include "enumdemo.h"

EnumDemo::EnumDemo() {}
DATA EnumDemo::initData(DATA_SIGN ds) { return D_IN; }
/*EnumDemo::*/ DATA_SIGN EnumDemo::setData(/*注释掉EnumDemo会是什么情况*/EnumDemo::DATA_SIGN ds) {

  int type = static_cast<int>(ds);
  // return type < 1 ? D_LITTLE : (type < 2 ? D_SMALL : D_BIG);
  return D_1;
}

在上面的代码中,如果不取消注释,编译器则会报“unknown type name DATA_SIGN”。这是什么原因呢?为什么在头文件中定义和函数内部使用都没有问题,返回值就不行呢?
这其实就涉及到了C++中对类函数的外部实现的签名处理问题。在C++的编译器中,函数的签名中的返回值类型处于全局作用域,这就意味着,对于类内部(一有层类的作用域限定符)定义的枚举,在自身内部应用或函数的内部实现中,是完全可以看到的。但返回值却无法看到,这就需要开发者增加一个作用域的限定空间(如上面代码中的注释掉的类作用域限定)。否则,编译器只能返回一个未知的类型名称的错误。
如果在全局再增加一个DATA_SIGN会是什么情况呢?如何让这两个DATA_SIGN混合使用?这就得明确全局作用域与类作用域内的区别。看声明中的“::”作用域符号。而在类声明和类实现中,如果没有明确的明白这种全局作用域符号则默认是使用类内定义的DATA_SIGN的。所以才会出现问题。
大家可以好好的把这种作用域的限制上机跑一下,可以更加清晰的掌握不同作用域内处理枚举的具体情况,能更深刻的理解C++对函数签名和默认推导机制的理解。

四、实际开发的处理

在实际的C++工程开发中,最好的处理方面在上面已经提到了。使用C++11中的枚举类。就可以避免一系列的问题。特别是,在实际的工程中,尽量避免枚举和枚举类的混合使用。如果一定有混合应用的情况下,最好采用设计了抽象的方式,将它们专门区别到不同的接口层中。
如果还有头铁的非要强硬的混合在一起,也可以引入using声明(但需要注意编译器的版本,最好在C++20及以上),也可以利用别名机制,来处理相关的命名冲突。当然,在细节上可能还是有一些需要开发者自己谨慎处理的情况,不能一概而论,马放南山(比如上面的DATA_SIGN,如果使用别名就可能与全局DATA_SIGN重名错误)。

五、总结

正如前面分析的,C++之所以为很多人诟病,一个重要原因就是细节太多。开发者经常不知道为什么编译正确又为什么编译不正确。糊里糊涂的程序就跑起来了。结果稍微一动,程序又跑动了。
其实这些都是表象,真正的原因就在对内部机制的不清楚。此次的枚举其实就是一个很好的例子,它其实是对C++函数签名和推导机制的一次具体的呈现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值