简介
枚举
到目前为止,我们主要使用数字和字符串。为现实世界建模时,有时希望变量只能取有限几个值,因此需要一种具有若干明确名称的专用类型。例如,滑板工厂的板面材料只能选择枫木、竹子或塑料。
可以用整数编码这些材料,但这需要额外检查系统是否传入非法数值。这样的魔数在代码中不易追踪,也容易混淆。枚举能使代码更易理解,并减少无意的比较错误。
使用示例
这种枚举称为有作用域枚举。下面展示了如何定义 DeckMaterial,注意 enum class 关键字及定义末尾的分号:
enum class DeckMaterial {
maple,
bamboo,
plastic
};
下面是滑板商店的定价函数,注意使用作用域解析运算符 ::,指定枚举类型中的某个枚举项:
double deck_price(double base_price, DeckMaterial material) {
if(material == DeckMaterial::plastic) {
return base_price * 0.9;
}
return base_price * 1.3;
}
作用域
假设还有第二个枚举,表示轮子材料:
enum class WheelMaterial {
steel,
clay,
plastic
};
虽然轮子和板面都可以由塑料制成,但两者不会混淆,因为 DeckMaterial 的塑料与 WheelMaterial 的塑料属于不同类型。每个枚举的枚举项都位于自己的作用域中,因此称为有作用域枚举。
进阶:无作用域枚举 既然存在有作用域枚举,也就有无作用域枚举。后者会把枚举项放入外围作用域,因此逐渐不如前者常用。如果两个无作用域枚举位于相同作用域,就不能同时具有上例中
plastic这样的同名枚举项。
无作用域枚举还会隐式转换为整数。观察下面这个可能出乎意料的结果:
enum CitrusFruits {
lemons, // 0
oranges, //1
};
enum IceCream {
walnut, // 0
apples, // 1
};
bool comparison{apples == oranges};
// => true
// Example from above:
bool comparison{DeckMaterial::plastic == WheelMaterial::plastic};
// => Does not compile!
若要把有作用域枚举转换为整数,可以使用 static_cast<int>。
switch 语句
与其他语言一样,C++ 提供 switch,可以简洁表达较长的 if ... else if 分支。使用 switch 后跟整型或枚举表达式,再用 case 列出各分支。还可提供 default,在没有其他分支匹配时执行。每个分支通常以 break 或 return 结束。
int price{0};
int adults{3};
int kids{2};
switch (int group_size{adults + kids}) {
case 1:
price = 50;
break;
case 2:
price = 70;
break;
default:
price = group_size * 30;
}
分支贯穿
switch 中的代码会继续执行,直到遇到 break 或 return 等结束操作。若未注意这一点,可能出现意外行为。
int adults{1};
int kids{0};
switch (int group_size{adults + kids}) {
case 1:
price = 50;
case 2:
price = 70;
default:
price = group_size * 30;
}
// price will be 30!
这种继续执行的机制也可以让多个标签共用同一段代码。例如,在预订应用中,2 人组和 3 人组可以调用同一个处理函数:
switch (group_size) {
case 1:
book_room();
break;
case 2:
case 3:
book_apartment(group_size);
break;
default:
book_house(group_size);
}
// book_apartment happens when group_size is 2 or 3
任务说明
朋友赫尔玛制作了一款迅速走红的小游戏 HellMath。社区吸引了一些捣乱者,使游戏和论坛氛围变差。她请你编写新的权限系统,把捣乱者与其他成员分开。
论坛支持三种操作:
- 阅读
- 发帖
- 删除
共有四类账号,默认权限各不相同:
- 访客:可以阅读帖子。
- 普通用户与捣乱者:可以阅读和发帖。
- 版主:可以阅读、发帖和删除,拥有全部权限。
赫尔玛发现直接封禁捣乱账号收效有限。她想让捣乱者以为自己的发言仍然有效,但帖子只对其他捣乱者可见。需要按优先级排序时,捣乱者始终排在最后;进入游戏时,也只能匹配其他捣乱者。
这种做法称为隐形封禁。
1. 定义账号类型与权限
定义 AccountStatus 枚举,包含四种账号类型:troll、guest、user、mod。
定义 Action 枚举,包含三种操作:read、write、remove。
2. 让捣乱者的帖子只对同类可见
论坛在每条帖子的元数据中记录发帖者的 AccountStatus。捣乱者的帖子只能显示给其他捣乱者。
实现 display_post,接收两个 AccountStatus 参数,分别表示发帖者和浏览者的账号状态,返回 bool。
using namespace hellmath;
display_post(AccountStatus::troll, AccountStatus::user);
// => false
display_post(AccountStatus::mod, AccountStatus::guest);
// => true
3. 检查不同账号的权限
赫尔玛需要判断某个账号是否有权执行指定操作。
实现 permission_check,第一个参数是 Action,第二个参数是待检查的 AccountStatus,按前述权限规则返回 bool。
permission_check(Action::remove, AccountStatus::guest);
// => false
permission_check(Action::write, AccountStatus::mod);
// => true
4. 检查游戏资格与玩家配对
为让玩家对自己的行为负责,HellMath 不允许访客进入游戏。捣乱者只能与其他捣乱者一起游戏,其他用户之间的连接不受限制。
实现 valid_player_combination,接收两个 AccountStatus 参数,判断两位玩家能否进入同一场游戏,返回 bool。
valid_player_combination(AccountStatus::guest, AccountStatus::mod);
// => false
valid_player_combination(AccountStatus::troll, AccountStatus::troll);
// => true
5. 建立优先队列
随着游戏与论坛规模扩大,赫尔玛需要分配算力和带宽。版主优先级最高,方便处理紧急情况;访客排在普通用户之后,捣乱者排在所有人之后。
实现 has_priority,接收两个 AccountStatus 参数。仅当第一个账号的优先级严格高于第二个时,返回 true。
has_priority(AccountStatus::guest, AccountStatus::mod);
// => false
has_priority(AccountStatus::user, AccountStatus::troll);
// => true