8-2 if 语句与代码块
我们将首先讨论的第一类控制流语句是条件语句conditional statement。条件语句用于指定关联语句是否应被执行。
C++支持两种基本类型的条件语句:if语句(我们在第4.10节——if语句介绍中已介绍过,本节将进一步探讨)和switch语句(将在后续几节中讲解)。
if语句快速回顾
C++中最基础的条件语句是if语句。if语句的结构如下:
if (condition)
true_statement;
或使用可选的 else 语句:
if (condition)
true_statement;
else
false_statement;
如果条件求值为真,则执行 true_statement。如果条件求值为假且存在可选的 else 语句,则执行 false_statement。
以下是一个使用 if 语句和可选 else 语句的简单程序:
#include <iostream>
int main()
{
std::cout << "Enter a number: ";
int x{};
std::cin >> x;
if (x > 10)
std::cout << x << " is greater than 10\n";
else
std::cout << x << " is not greater than 10\n";
return 0;
}
这个程序的工作方式完全符合你的预期:


if or else 的多重条件语句
新手程序员常会尝试如下写法:
#include <iostream>
namespace constants
{
constexpr int minRideHeightCM { 140 };
}
int main()
{
std::cout << "Enter your height (in cm): ";
int x{};
std::cin >> x;
if (x >= constants::minRideHeightCM)
std::cout << "You are tall enough to ride.\n";
else
std::cout << "You are not tall enough to ride.\n";
std::cout << "Too bad!\n"; // focus on this line
return 0;
}
然而,请考虑程序的以下运行过程:

该程序无法按预期运行,因为 true_statement 和 false_statement 只能是单个语句。这里的缩进误导了我们——上述程序的执行效果如同以下写法:
#include <iostream>
namespace constants
{
constexpr int minRideHeightCM { 140 };
}
int main()
{
std::cout << "Enter your height (in cm): ";
int x{};
std::cin >> x;
if (x >= constants::minRideHeightCM)
std::cout << "You are tall enough to ride.\n";
else
std::cout << "You are not tall enough to ride.\n";
std::cout << "Too bad!\n"; // focus on this line
return 0;
}
这更明确地说明了“太糟糕了!”语句将始终执行。
然而,通常需要根据某些条件执行多个语句。为此,我们可以使用复合语句(代码块):
#include <iostream>
namespace constants
{
constexpr int minRideHeightCM { 140 };
}
int main()
{
std::cout << "Enter your height (in cm): ";
int x{};
std::cin >> x;
if (x >= constants::minRideHeightCM)
std::cout << "You are tall enough to ride.\n";
else
{ // note addition of block here
std::cout << "You are not tall enough to ride.\n";
std::cout << "Too bad!\n";
}
return 0;
}
请记住,代码块会被视为单个语句,因此现在它会按预期运行:


隐式代码块
若程序员未在if语句或else语句的语句部分声明代码块,编译器将隐式声明一个代码块。因此:
if (condition)
true_statement;
else
false_statement;
实际上相当于:
if (condition)
{
true_statement;
}
else
{
false_statement;
}
大多数时候,这并不重要。然而,新手程序员有时会尝试在隐式代码块中定义变量,例如:
#include <iostream>
int main()
{
if (true)
int x{ 5 };
else
int x{ 6 };
std::cout << x << '\n';
return 0;
}

这段代码无法编译,编译器会报错指出标识符 x 未定义。这是因为上述示例等同于:
#include <iostream>
int main()
{
if (true)
{
int x{ 5 };
} // x destroyed here
else
{
int x{ 6 };
} // x destroyed here
std::cout << x << '\n'; // x isn't in scope here
return 0;
}
在此上下文中,变量 x 的作用域为块作用域,并在块结束时被销毁。当执行到 std::cout 语句时,x 已不复存在。
是否应为单条语句添加代码块
程序员社区中存在争议:if或else语句后的单条语句是否应显式包裹在代码块中。
支持使用代码块的理由众多。
- 不使用代码块容易导致无意间添加看似条件语句实则不然的代码。例如:
if (age >= minDrinkingAge)
purchaseBeer();
现在假设我们赶时间,修改这个程序以添加另一项功能:
if (age >= minDrinkingAge)
purchaseBeer();
gamble(); // will always execute
哎呀,我们刚刚允许未成年人赌博了。祝你在监狱里玩得开心!
- 不使用代码块会使程序更难调试。假设我们有以下代码片段:
if (age >= minDrinkingAge)
addBeerToCart(); // conditionally executes
checkout(); // always executes
假设我们怀疑 addBeerToCart() 函数存在问题,于是将其注释掉:
if (age >= minDrinkingAge)
// addBeerToCart();
checkout(); // conditionally executes now
现在我们让checkout()变成了条件函数,这显然不是我们的本意。
若始终在if或else语句后使用代码块,上述情况均不会发生。
- 如果条件语句(C++23新增的if语句变体)要求使用代码块,那么使用代码块能确保if语句与if条件语句的一致性。
反对在单条语句外使用代码块的最佳理由是:添加代码块会通过垂直间隔分散代码,导致单次可见范围缩小,从而降低代码可读性并可能引发其他更严重的错误。
尽管此建议并非绝对普遍,但开发者社群似乎更倾向于始终使用代码块。
最佳实践
建议将与if或else相关的单个语句放在代码块中(尤其在学习阶段)。经验丰富的C++开发者有时会忽略此做法,以换取更紧凑的垂直间距。
一个合理的折中方案是将单行语句与if或else语句置于同一行:
if (age >= minDrinkingAge) purchaseBeer();
else std::cout << "No drinky for you\n".
这种方法在可读性上稍有损失,却能同时规避上述两种弊端。
单行方法的一个合理批评在于其生成的代码更难调试:
- 由于条件语句和关联语句会在同一执行步骤中运行,难以判断语句究竟是实际执行还是被跳过。
- 由于条件语句与关联语句位于同一行,无法仅对关联语句设置断点(即仅在语句实际执行时暂停程序)。
但若上述任一问题妨碍调试,可在条件语句与语句间插入换行符(使其分开成行),完成调试后再移除该换行符。
if-else 与 if-if 的区别
新手程序员有时会困惑何时该使用 if-else(if 语句后跟一个或多个 else 语句),何时该使用 if-if(if 语句后跟一个或多个额外的 if 语句)。
- 当你只想执行第一个满足条件的后续代码时,使用 if-else。
- 当你希望执行所有满足条件的后续代码时,使用 if-if。
以下程序演示了这种区别:
#include <iostream>
void ifelse(bool a, bool b, bool c)
{
if (a) // always evaluates
std::cout << "a";
else if (b) // only evaluates when prior if-statement condition is false
std::cout << "b";
else if (c) // only evaluates when prior if-statement condition is false
std::cout << "c";
std::cout << '\n';
}
void ifif(bool a, bool b, bool c)
{
if (a) // always evaluates
std::cout << "a";
if (b) // always evaluates
std::cout << "b";
if (c) // always evaluates
std::cout << "c";
std::cout << '\n';
}
int main()
{
ifelse(false, true, true);
ifif(false, true, true);
return 0;
}

在调用 ifelse(false, true, true) 时,a 为假,因此不执行关联语句,转而执行关联的 else 语句。b 为真,故输出 b。由于此 if 条件为真,关联的 else 语句不会执行(紧随其后的其他 else 语句同样不会执行)。请注意,我们仅执行了第一个真条件(b)之后的代码。
在调用 ifif(false, true, true) 时,a 为假,因此不执行关联语句,转至下一个 if。b 为真,因此打印 b 并转至下一个 if。c 为真,因此打印 c。注意我们执行了所有真条件(b 和 c)之后的代码。
现在观察这个类似的函数:
char getFirstMatchingChar(bool a, bool b, bool c)
{
if (a) // always evaluates
return 'a';
else if (b) // only evaluates when prior if-statement condition is false
return 'b';
else if (c) // only evaluates when prior if-statement condition is false
return 'c';
return 0;
}
既然我们使用了if-else结构,显然只希望在第一个条件为真时执行后续代码。但当关联语句均返回值时,我们可以改写为:
char getFirstMatchingChar(bool a, bool b, bool c)
{
if (a) // always evaluates
return 'a'; // returns when if-statement is true
if (b) // only evaluates when prior if-statement condition is false
return 'b'; // returns when if-statement is true
if (c) // only evaluates when prior if-statement condition is false
return 'c'; // returns when if-statement is true
return 0;
}
虽然表面上看起来像是希望在每个真条件后执行代码,但一旦遇到第一个真条件,其关联语句就会触发函数返回。剩余的if语句将永远不会被评估。因此,这种写法与先前版本的行为完全等效。当每个关联语句都返回值时,许多程序员倾向于省略else关键字,这样既能避免冗余代码,又能使条件语句排列更整齐。
关键要点
当所有关联语句都返回值时,你始终可以仅使用if-if结构,因为else语句本身不提供任何值。
我们将在下一课继续探索if语句的相关内容。

浙公网安备 33010602011771号