设想这样一种场景:我们需要向用户展示一个菜单,并要求其做出选择;若用户输入了无效选项,则必须再次询问。显然,菜单及选择过程应当置于某种循环之中(以便反复提示用户,直至其输入有效数据),但究竟应选择哪一种循环?
由于 while 循环在入口处即评估条件,这种需求对它来说颇为别扭。我们可以勉强写成如下形式:
#include <iostream>
int main()
{
// selection 必须在 while 循环外声明,以便后续使用
int selection {}; // 值初始化为 0
while (selection < 1 || selection > 4)
{
std::cout << "Please make a selection: \n";
std::cout << "1) Addition\n";
std::cout << "2) Subtraction\n";
std::cout << "3) Multiplication\n";
std::cout << "4) Division\n";
std::cin >> selection;
}
// 在此处根据 selection 执行操作
// 例如使用 switch 语句
std::cout << "You selected option #" << selection << '\n';
return 0;
}
然而,这种方法仅因我们将 selection 的初值设为 0,而 0 并不在合法取值集合 {1, 2, 3, 4} 内才得以成立。倘若 0 也是合法选项,我们就必须另选一个初始化值来表示“无效”,从而把“魔数”(5.2 — 字面量)引入代码。
另一种做法是增加一个专门用于控制循环的布尔变量:
#include <iostream>
int main()
{
int selection {};
bool invalid { true }; // 仅用于控制循环的新变量
while (invalid)
{
std::cout << "Please make a selection: \n";
std::cout << "1) Addition\n";
std::cout << "2) Subtraction\n";
std::cout << "3) Multiplication\n";
std::cout << "4) Division\n";
std::cin >> selection;
invalid = (selection < 1 || selection > 4);
}
// 在此处根据 selection 执行操作
// 例如使用 switch 语句
std::cout << "You selected option #" << selection << '\n';
return 0;
}
虽然此法避免了魔数,却额外引入了一个变量,既增加了复杂度,也带来了新的出错可能。
do-while 语句
为解决上述问题,C++ 提供了 do-while 语句:
do
statement; // 可为单条语句,也可为复合语句
while (condition);
do-while 语句与 while 循环相似,区别在于无论条件如何,循环体至少执行一次。语句执行完毕后,do-while 再检查条件;若条件为 true,则控制流跳回 do-while 顶部并再次执行。
以下示例使用 do-while 改写前述代码:
#include <iostream>
int main()
{
// selection 必须在 do-while 循环外声明,以便后续使用
int selection {};
do
{
std::cout << "Please make a selection: \n";
std::cout << "1) Addition\n";
std::cout << "2) Subtraction\n";
std::cout << "3) Multiplication\n";
std::cout << "4) Division\n";
std::cin >> selection;
}
while (selection < 1 || selection > 4);
// 在此处根据 selection 执行操作
// 例如使用 switch 语句
std::cout << "You selected option #" << selection << '\n';
return 0;
}
如此,我们既避免了魔数,也无需额外的布尔变量。
值得注意:变量 selection 必须在 do 块之外声明。若在 do 块内声明,则当 do 块结束、条件尚未评估时,该变量已被销毁;而条件表达式仍需使用该变量,因此必须将其置于 do 块之前(即使函数后续不再使用该变量亦须如此)。
实践中,do-while 循环并不常用。由于条件位于循环底部,易使循环条件不醒目,进而导致错误。因此,许多开发者建议完全避免 do-while。我们持较温和立场:在二者可选的情况下,优先使用 while 循环。
最佳实践
在可选的情况下,优先使用 while 循环而非 do-while。