我们将首先讨论控制流语句中的第一类——条件语句。条件语句用于指定在何种情况下应执行与之关联的一条或多条语句。
C++ 支持两种基本的条件语句:if
语句(《if 语句简介》中介绍,本节将继续展开)和 switch
语句(将在后续两节讲解)。
if 语句快速回顾
C++ 中最基本的条件语句是 if
语句,其语法形式如下:
if (condition)
true_statement;
或带可选的 else
分支:
if (condition)
true_statement;
else
false_statement;
若条件求值为真,则执行 true_statement
;若条件求值为假且存在 else
分支,则执行 false_statement
。
以下示例程序展示了带 else
的 if
语句:
#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;
}
程序运行结果符合预期:
Enter a number: 15
15 is greater than 10
Enter a number: 4
4 is not greater than 10
if 或 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"; // 关注此行
return 0;
}
然而,程序的一次运行结果如下:
Enter your height (in cm): 180
You are tall enough to ride.
Too bad!
结果不符合预期,原因在于 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"; // 始终执行
return 0;
}
由此可见,“Too bad!” 总会被执行。
若需根据条件执行多条语句,应使用复合语句(即语句块):
#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";
}
return 0;
}
由于语句块被视为单条语句,程序现可正确运行:
Enter your height (in cm): 180
You are tall enough to ride.
Enter your height (in cm): 130
You are not tall enough to ride.
Too bad!
隐式语句块
若程序员未在 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 在此处销毁
else
{
int x{ 6 };
} // x 在此处销毁
std::cout << x << '\n'; // x 已离开作用域
return 0;
}
在此情形下,x
具有语句块作用域,并在块结束时被销毁;到达 std::cout
语句时,x
已不存在。
if 或 else 后单条语句是否加块的取舍
程序员社区对单条语句是否显式加块存在争议。
支持始终加块的理由:
不加块易在无意中添加看似受条件控制、实则不受的语句。例如:
if (age >= minDrinkingAge) purchaseBeer();
匆忙中追加功能:
if (age >= minDrinkingAge) purchaseBeer(); gamble(); // 始终执行
结果未成年人也能赌博,后果严重!
不加块可能增加调试难度:
if (age >= minDrinkingAge) addBeerToCart(); // 条件执行 checkout(); // 始终执行
若怀疑
addBeerToCart()
出错而将其注释:if (age >= minDrinkingAge) // addBeerToCart(); checkout(); // 意外变为条件执行
若始终加块,上述问题均可避免。
C++23 引入的
if constexpr
要求必须使用块,因此加块可保持if
与if constexpr
的一致性。
反对加块的主要理由:
- 加块会垂直拉长代码,减少一屏可见行数,降低可读性,可能引发更严重错误。
社区观点更倾向于始终加块,尽管该建议尚未成为普遍共识。
最佳实践
初学者宜将与 if
或 else
关联的单条语句置于块内。经验丰富的开发者有时为获得更紧凑的垂直间距可不遵循此做法。
折中方案:将单行语句与 if
或 else
置于同一行:
if (age >= minDrinkingAge) purchaseBeer();
else std::cout << "No drinky for you\n";
该方法在牺牲少量可读性的同时避免了前述两个问题。
对单行写法的一个合理批评是调试困难:
- 条件与语句在同一执行步骤完成,难以判断语句是否实际执行。
- 二者位于同一行,无法在语句处单独设断点。
若调试时受上述限制,可在条件与语句间插入换行(分占两行),调试后再恢复。
if-else 与连续 if 的选择
初学者常困惑何时使用 if-else
(if
后接若干 else
)与连续 if
(多个并列 if
)。
- 若仅希望执行首个为真条件后的代码,应使用
if-else
。 - 若希望执行所有为真条件后的代码,应使用连续
if
。
示例如下:
#include <iostream>
void ifelse(bool a, bool b, bool c)
{
if (a) // 始终求值
std::cout << "a";
else if (b) // 仅当先前条件为假时求值
std::cout << "b";
else if (c) // 仅当先前条件为假时求值
std::cout << "c";
std::cout << '\n';
}
void ifif(bool a, bool b, bool c)
{
if (a) // 始终求值
std::cout << "a";
if (b) // 始终求值
std::cout << "b";
if (c) // 始终求值
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
,后续 else
不再执行。仅首个为真条件后的代码被执行。
调用 ifif(false, true, true)
时,a
为假,跳过;b
为真,输出 b
;c
为真,输出 c
。所有为真条件后的代码均被执行。
再看下例:
char getFirstMatchingChar(bool a, bool b, bool c)
{
if (a) // 始终求值
return 'a';
else if (b) // 仅当先前条件为假时求值
return 'b';
else if (c) // 仅当先前条件为假时求值
return 'c';
return 0;
}
由于使用 if-else
,仅首个为真条件后的代码被执行。但当每条关联语句均返回值时,可改写为:
char getFirstMatchingChar(bool a, bool b, bool c)
{
if (a) // 始终求值
return 'a'; // 条件为真即返回
if (b) // 仅当先前条件为假时求值
return 'b'; // 条件为真即返回
if (c) // 仅当先前条件为假时求值
return 'c'; // 条件为真即返回
return 0;
}
表面看似连续 if
,然而一旦首个条件为真,函数即返回,后续 if
不再求值,因此行为与前述版本等价。当所有关联语句均返回值时,多数程序员倾向省略 else
,以减少冗余并使条件对齐更佳。
核心洞见
若所有关联语句均返回值,可直接使用连续 if
,此时 else
并无额外价值。
下一节将继续深入探讨 if
语句。