第4章 复合语句和控制语句 复合语句(语句块) 复合语句是由一对大括号{}包围的一组语句,也称为语句块:
1 2 3 4 5 6 7 8 { int x = 10 ; int y = 20 ; int sum = x + y; std::cout << "Sum: " << sum << std::endl; }
语句块的特点:
作用域 :语句块内声明的变量只在语句块内有效嵌套 :语句块可以嵌套使用空语句块 :空的语句块是合法的,不执行任何操作条件语句 if 语句 基本形式 if-else 形式 1 2 3 4 5 if (condition) { } else { }
if-else if-else 形式 1 2 3 4 5 6 7 if (condition1) { } else if (condition2) { } else { }
嵌套 if 语句 1 2 3 4 5 6 7 8 9 if (condition1) { if (condition2) { } else { } } else { }
if 语句的初始化语句(C++17+) C++17引入了if语句的初始化语句,允许在if语句中声明和初始化变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if (int x = getValue (); x > 0 ) { std::cout << "x is positive: " << x << std::endl; } else { std::cout << "x is non-positive: " << x << std::endl; } if (auto ptr = std::make_unique <Person>("Alice" , 30 ); ptr) { ptr->display (); } std::vector<int > numbers = {1 , 2 , 3 , 4 , 5 }; if (auto it = std::find (numbers.begin (), numbers.end (), 3 ); it != numbers.end ()) { std::cout << "Found: " << *it << std::endl; }
if constexpr 语句(C++17+) C++17引入的if constexpr语句,用于编译时条件判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 template <typename T>void printTypeInfo (T value) { if constexpr (std::is_integral_v<T>) { std::cout << "Integral type: " << value << std::endl; } else if constexpr (std::is_floating_point_v<T>) { std::cout << "Floating point type: " << value << std::endl; } else if constexpr (std::is_same_v<T, std::string>) { std::cout << "String type: " << value << std::endl; } else { std::cout << "Other type" << std::endl; } } printTypeInfo (42 ); printTypeInfo (3.14 ); printTypeInfo (std::string ("Hello" ));
if constexpr的特点:
编译时执行 :条件在编译时求值,未满足的分支会被完全忽略类型安全 :可以在不同分支中使用不同类型的代码零开销 :未使用的分支不会生成任何代码模板友好 :特别适合在模板代码中根据类型执行不同操作switch 语句 1 2 3 4 5 6 7 8 9 10 11 12 switch (expression) { case value1: break ; case value2: break ; default : break ; }
switch语句的特点:
表达式类型 :表达式必须是整型、字符型或枚举类型case标签 :每个case标签必须是常量表达式break语句 :如果没有break语句,程序会继续执行下一个casedefault分支 :可选的,处理所有未匹配的情况switch 语句的初始化语句(C++17+) C++17引入了switch语句的初始化语句,允许在switch语句中声明和初始化变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 switch (int day = getDayOfWeek (); day) { case 1 : std::cout << "Monday" << std::endl; break ; case 2 : std::cout << "Tuesday" << std::endl; break ; default : std::cout << "Invalid day" << std::endl; break ; } switch (auto ptr = createObject (); ptr->getType ()) { case ObjectType::TypeA: ptr->processTypeA (); break ; case ObjectType::TypeB: ptr->processTypeB (); break ; default : ptr->processDefault (); break ; }
switch 语句与枚举类 使用强类型枚举(enum class)可以提高switch语句的类型安全性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 enum class Color { Red, Green, Blue }; void printColor (Color color) { switch (color) { case Color::Red: std::cout << "Red" << std::endl; break ; case Color::Green: std::cout << "Green" << std::endl; break ; case Color::Blue: std::cout << "Blue" << std::endl; break ; } }
switch 语句与模式匹配(C++26 预览) C++26将引入模式匹配,使switch语句更加强大:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct Point { int x; int y; }; void processPoint (const Point& p) { switch (p) { case {0 , 0 }: std::cout << "Origin" << std::endl; break ; case {x, 0 }: std::cout << "On x-axis: " << x << std::endl; break ; case {0 , y}: std::cout << "On y-axis: " << y << std::endl; break ; case {x, y}: std::cout << "Point: (" << x << ", " << y << ")" << std::endl; break ; } }
条件运算符(三元运算符) 1 condition ? expression1 : expression2;
如果条件为真,执行expression1并返回其结果;否则执行expression2并返回其结果。
循环语句 while 循环 while循环的特点:
先判断后执行 :条件为真时才执行循环体可能不执行 :如果初始条件为假,循环体一次也不执行do-while 循环 1 2 3 do { } while (condition);
do-while循环的特点:
先执行后判断 :至少执行一次循环体条件为真时继续 :执行完循环体后,条件为真则继续循环for 循环 1 2 3 for (initialization; condition; update) { }
for循环的执行过程:
执行初始化语句 判断条件,如果为假,结束循环 执行循环体 执行更新语句 回到步骤2 范围 for 循环(C++11+) 1 2 3 for (declaration : collection) { }
范围for循环用于遍历容器、数组等集合类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 std::vector<int > numbers = {1 , 2 , 3 , 4 , 5 }; for (int num : numbers) { std::cout << num << " " ; } for (int & num : numbers) { num *= 2 ; } for (const int & num : numbers) { std::cout << num << " " ; }
范围 for 循环的初始化语句(C++20+) C++20引入了范围for循环的初始化语句,允许在循环开始前声明和初始化变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 for (int i = 0 ; auto & num : numbers) { std::cout << "Index " << i << ": " << num << std::endl; i++; } for (auto temp = createTemporaryData (); auto & item : temp) { processItem (item); } std::map<std::string, int > scores = { {"Alice" , 95 }, {"Bob" , 87 }, {"Charlie" , 92 } }; for (auto it = scores.begin (); auto & [name, score] : scores) { std::cout << "Rank " << std::distance (scores.begin (), it) + 1 << ": " << name << " - " << score << std::endl; ++it; }
范围 for 循环与结构化绑定(C++17+) 结合结构化绑定,范围for循环可以更方便地遍历键值对:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 std::map<std::string, int > scores = { {"Alice" , 95 }, {"Bob" , 87 }, {"Charlie" , 92 } }; for (const auto & [name, score] : scores) { std::cout << name << ": " << score << std::endl; } std::vector<std::tuple<std::string, int , double >> students = { {"Alice" , 20 , 3.8 }, {"Bob" , 21 , 3.5 }, {"Charlie" , 19 , 3.9 } }; for (const auto & [name, age, gpa] : students) { std::cout << "Name: " << name << ", Age: " << age << ", GPA: " << gpa << std::endl; } struct Point { int x; int y; }; std::vector<Point> points = { {1 , 2 }, {3 , 4 }, {5 , 6 } }; for (const auto & [x, y] : points) { std::cout << "Point: (" << x << ", " << y << ")" << std::endl; }
范围 for 循环与视图(C++20+) 结合C++20的Ranges库,范围for循环可以更灵活地处理数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <ranges> #include <vector> #include <iostream> int main () { std::vector<int > numbers = {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 }; auto evenDoubled = numbers | std::views::filter ([](int n) { return n % 2 == 0 ; }) | std::views::transform ([](int n) { return n * 2 ; }); for (int num : evenDoubled) { std::cout << num << " " ; } auto reversed = numbers | std::views::reverse; for (int num : reversed) { std::cout << num << " " ; } auto sliced = numbers | std::views::drop (2 ) | std::views::take (5 ); for (int num : sliced) { std::cout << num << " " ; } return 0 ; }
跳转语句 break 语句 break语句用于:
跳出循环 :提前结束for、while、do-while循环跳出switch语句 :结束switch语句的执行1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for (int i = 0 ; i < 10 ; i++) { if (i == 5 ) { break ; } std::cout << i << " " ; } switch (grade) { case 'A' : std::cout << "Excellent" << std::endl; break ; case 'B' : std::cout << "Good" << std::endl; break ; default : std::cout << "Other" << std::endl; break ; }
continue 语句 continue语句用于跳过本次循环的剩余部分,直接进入下一次循环:
1 2 3 4 5 6 7 for (int i = 0 ; i < 10 ; i++) { if (i % 2 == 0 ) { continue ; } std::cout << i << " " ; }
return 语句 return语句用于:
结束函数执行 :立即退出当前函数返回值 :可选地返回一个值给函数调用者1 2 3 4 5 6 7 8 9 10 void printHello () { std::cout << "Hello" << std::endl; return ; } int add (int a, int b) { return a + b; }
return 语句与移动语义(C++11+) 1 2 3 4 5 6 7 8 9 10 std::vector<int > createVector () { std::vector<int > v = {1 , 2 , 3 , 4 , 5 }; return v; } std::string createString () { return "Hello, World!" ; }
return 语句与结构化绑定(C++17+) 1 2 3 4 5 6 7 8 std::tuple<int , double , std::string> getValues () { return {42 , 3.14 , "Hello" }; } auto [i, d, s] = getValues ();std::cout << i << ", " << d << ", " << s << std::endl;
[[noreturn]] 属性(C++11+) C++11引入的[[noreturn]]属性,用于标记不会返回的函数:
1 2 3 4 5 6 7 8 9 10 11 [[noreturn]] void fatalError (const std::string& message) { std::cerr << "Fatal error: " << message << std::endl; std::exit (EXIT_FAILURE); } [[noreturn]] void infiniteLoop () { while (true ) { } }
goto 语句 goto语句用于无条件跳转到函数内的标签位置:
1 2 3 4 5 6 7 8 9 if (error) { goto errorHandler; } std::cout << "No error" << std::endl; errorHandler: std::cout << "Error occurred" << std::endl;
注意 :goto语句可能会使代码结构混乱,应尽量避免使用。
循环控制的高级技巧 多重循环 多重循环是指循环的嵌套使用:
1 2 3 4 5 6 7 for (int i = 1 ; i <= 9 ; i++) { for (int j = 1 ; j <= i; j++) { std::cout << j << "*" << i << "=" << i*j << "\t" ; } std::cout << std::endl; }
循环中的 break 和 continue 在多重循环中,break和continue只影响最内层的循环:
1 2 3 4 5 6 7 8 for (int i = 0 ; i < 5 ; i++) { for (int j = 0 ; j < 5 ; j++) { if (j == 2 ) { break ; } std::cout << "i=" << i << ", j=" << j << std::endl; } }
使用标志变量控制循环 1 2 3 4 5 6 7 8 9 10 bool found = false ;for (int i = 0 ; i < 10 && !found; i++) { for (int j = 0 ; j < 10 ; j++) { if (i == 5 && j == 5 ) { found = true ; break ; } std::cout << "i=" << i << ", j=" << j << std::endl; } }
异常处理 try-catch 语句 1 2 3 4 5 6 7 8 9 10 11 12 try { if (someCondition) { throw std::exception ("Error occurred" ); } } catch (const std::exception& e) { std::cout << "Exception caught: " << e.what () << std::endl; } catch (...) { std::cout << "Unknown exception caught" << std::endl; }
抛出异常 1 2 3 4 5 6 7 8 void validateAge (int age) { if (age < 0 ) { throw std::invalid_argument ("Age cannot be negative" ); } if (age > 120 ) { throw std::out_of_range ("Age is out of range" ); } }
使用std::source_location(C++20+) C++20引入的std::source_location类用于获取源代码位置信息,可用于异常处理中提供更详细的错误位置。
使用案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> #include <stdexcept> #include <source_location> class MyException : public std::exception {private : std::string message; std::source_location location; public : MyException (const std::string& msg, const std::source_location& loc = std::source_location::current ()) : message (msg), location (loc) {} const char * what () const noexcept override { return message.c_str (); } const std::source_location& getLocation () const { return location; } }; void riskyOperation (int value, const std::source_location& loc = std::source_location::current()) { if (value < 0 ) { throw MyException ("Negative value not allowed" , loc); } } int main () { try { riskyOperation (-1 ); } catch (const MyException& e) { const auto & loc = e.getLocation (); std::cout << "Exception caught: " << e.what () << std::endl; std::cout << "File: " << loc.file_name () << std::endl; std::cout << "Line: " << loc.line () << std::endl; std::cout << "Function: " << loc.function_name () << std::endl; } return 0 ; }
异常规格说明(C++11前) 1 2 3 4 5 6 7 8 9 void func () throw (int , std::exception) { } void func () throw () { }
noexcept 说明符(C++11+) 1 2 3 4 5 6 7 8 9 10 11 12 void func () noexcept { } void func () noexcept (condition) { } bool isNoexcept = noexcept (func ());
控制语句的最佳实践 1. 代码可读性 缩进 :使用一致的缩进风格空格 :在运算符两侧、逗号后添加空格括号 :即使只有一条语句,也使用大括号包围注释 :为复杂的条件和循环添加注释2. 避免深度嵌套 提取函数 :将复杂的嵌套代码提取为单独的函数使用早期返回 :对于简单的错误情况,使用早期返回简化条件 :将复杂的条件表达式分解为多个简单的条件3. 循环优化 减少循环内计算 :将循环不变的计算移到循环外避免循环内的内存分配 :在循环外分配内存使用合适的循环类型 :根据具体情况选择for、while或do-while循环考虑使用STL算法 :对于常见的循环模式,使用STL算法4. 错误处理 使用异常 :对于可恢复的错误,使用异常处理使用错误码 :对于性能敏感的代码,使用错误码避免使用goto :尽量使用结构化的控制语句资源管理 :使用RAII(资源获取即初始化)管理资源常见错误和陷阱 1. 无限循环 1 2 3 4 5 6 7 8 9 10 11 while (true ) { std::cout << "Hello" << std::endl; } for (int i = 0 ; i < 10 ; j++) { std::cout << i << std::endl; }
2. 边界条件错误 1 2 3 4 5 6 7 8 9 10 11 int arr[5 ];for (int i = 0 ; i <= 5 ; i++) { arr[i] = i; } std::string s = "Hello" ; for (int i = 0 ; i <= s.length (); i++) { std::cout << s[i]; }
3. 逻辑错误 1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (x = 5 ) { std::cout << "x is 5" << std::endl; } if (a && b || c) { } if (x == 0.1 ) { std::cout << "x is 0.1" << std::endl; }
4. 语法错误 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 if (x > y) { std::cout << "x is greater than y" } if (x > y { std::cout << "x is greater than y" << std::endl; } switch (x) { case 1 std::cout << "x is 1" << std::endl; break ; }
小结 本章介绍了C++中的复合语句和控制语句,包括:
复合语句 :由大括号包围的语句块,用于创建局部作用域条件语句 :if、if-else、if-else if-else和switch语句,用于根据条件执行不同的代码循环语句 :while、do-while、for和范围for循环,用于重复执行代码跳转语句 :break、continue、return和goto语句,用于控制程序的执行流程异常处理 :try-catch语句,用于处理运行时错误控制语句是C++程序的重要组成部分,它们使程序能够根据不同的条件执行不同的操作,或者重复执行某些操作。掌握好控制语句的使用方法,对于编写结构清晰、逻辑正确的程序至关重要。
在使用控制语句时,应注意代码的可读性和可维护性,避免深度嵌套和复杂的条件表达式。同时,要注意处理边界条件和异常情况,确保程序的健壮性。
在后续章节中,我们将学习函数、数组、指针等更高级的C++特性,这些特性将与控制语句结合使用,帮助我们构建更复杂、更强大的程序。