Ch 12: 函数进阶
- 掌握递归函数的写法,理解尾递归优化
- 理解默认参数的全部陷阱(重载二义性、副作用)
- 学会使用 constexpr 进行编译期计算
- 掌握内联函数的适用场景和注意事项
- 理解函数指针和 std::function 的区别和使用场景
12.1 递归函数
Section titled “12.1 递归函数”Python 递归回顾
Section titled “Python 递归回顾”# Python - 简单递归def factorial(n): if n <= 1: return 1 return n * factorial(n - 1)
# 斐波那契def fibonacci(n): if n <= 1: return n return fibonacci(n - 1) + fibonacci(n - 2)
# 树形递归(效率低)result = fibonacci(35) # 需要数百万次调用C++ 递归基础
Section titled “C++ 递归基础”// C++ 递归 - 语法与 Python 类似int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1);}
// 斐波那契 - 注意效率问题int fibonacci(int n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2);}
// 调用int main() { int f = factorial(5); // 120 int fib = fibonacci(10); // 55}递归深度和栈帧
Section titled “递归深度和栈帧”#include <iostream>
// 递归深度受限于栈大小void countdown(int n) { std::cout << n << std::endl; if (n > 1) { countdown(n - 1); // 每次调用消耗栈空间 }}
int main() { countdown(5); // 5, 4, 3, 2, 1
// 大递归会导致栈溢出 // countdown(100000); // 很可能崩溃 return 0;}# Python - 没有尾递归优化(截至 Python 3.x)def factorial_tail(n, accumulator=1): if n == 0: return accumulator return factorial_tail(n - 1, n * accumulator) # 仍然是栈累积// C++ - 尾递归(编译器可能优化,也可能不优化)int factorial_tail(int n, int accumulator = 1) { if (n <= 1) return accumulator; return factorial_tail(n - 1, n * accumulator); // 尾调用}
// 斐波那契尾递归int fibonacci_tail(int n, int a = 0, int b = 1) { if (n == 0) return a; return fibonacci_tail(n - 1, b, a + b);}
int main() { // 注意:C++ 标准不保证尾递归优化 // GCC/Clang 在优化模式下可能优化 std::cout << fibonacci_tail(20) << std::endl; // 6765}关键点:C++ 不保证尾递归优化,编译器说了算。Python 也不保证。选择循环还是递归取决于可读性和栈深度需求。
递归 vs 迭代
Section titled “递归 vs 迭代”#include <vector>
// 递归版本 - 直观但有栈溢出风险int fibonacci_recursive(int n) { if (n <= 1) return n; return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2);}
// 迭代版本 - 无栈溢出风险int fibonacci_iterative(int n) { if (n <= 1) return n; int a = 0, b = 1; for (int i = 2; i <= n; ++i) { int temp = a + b; a = b; b = temp; } return b;}
// 使用记忆化的递归(C++20)#include <unordered_map>
int fibonacci_memo(int n, std::unordered_map<int, int>& memo) { if (n <= 1) return n; if (auto it = memo.find(n); it != memo.end()) { return it->second; } int result = fibonacci_memo(n - 1, memo) + fibonacci_memo(n - 2, memo); memo[n] = result; return result;}递归的实际应用场景
Section titled “递归的实际应用场景”#include <iostream>#include <vector>#include <string>
// 1. 二叉树遍历(递归天然适合)struct TreeNode { int value; TreeNode* left; TreeNode* right; TreeNode(int v) : value(v), left(nullptr), right(nullptr) {}};
void inorder(TreeNode* node) { if (!node) return; inorder(node->left); std::cout << node->value << " "; inorder(node->right);}
void preorder(TreeNode* node) { if (!node) return; std::cout << node->value << " "; preorder(node->left); preorder(node->right);}
// 2. 文件系统遍历(模拟)void walk_directory(const std::string& path, int depth = 0) { // 实际需要文件系统 API,这里简化 std::string indent(depth * 2, ' '); std::cout << indent << path << std::endl; // 递归处理子目录 // walk_directory(subpath, depth + 1);}
// 3. 全排列void permute(std::vector<int>& nums, int start, std::vector<std::vector<int>>& results) { if (start == nums.size()) { results.push_back(nums); return; } for (int i = start; i < nums.size(); ++i) { std::swap(nums[start], nums[i]); permute(nums, start + 1, results); std::swap(nums[start], nums[i]); // 回溯 }}
std::vector<std::vector<int>> generate_permutations(std::vector<int> nums) { std::vector<std::vector<int>> results; permute(nums, 0, results); return results;}12.2 默认参数陷阱
Section titled “12.2 默认参数陷阱”Python 可变默认参数(著名陷阱)
Section titled “Python 可变默认参数(著名陷阱)”# Python - 可变默认参数是共享的!def append_to(item, target=[]): target.append(item) return target
print(append_to(1)) # [1]print(append_to(2)) # [1, 2] ← BUG!应该是 [2]print(append_to(3)) # [1, 2, 3] ← 累积 Bug
# 正确写法def append_to(item, target=None): if target is None: target = [] target.append(item) return target
print(append_to(1)) # [1]print(append_to(2)) # [2]print(append_to(3)) # [3]C++ 默认参数详解
Section titled “C++ 默认参数详解”// C++ - 默认参数是固定值,没有 Python 的问题#include <vector>#include <string>
// 每次调用都会创建新的空 vectorvoid append_to(int item, std::vector<int> target = {}) { target.push_back(item); // 注意:这是拷贝!不影响调用者}
// 如果想修改原对象,用引用void append_to_ref(int item, std::vector<int>& target = ???) { // 错误!不能引用默认参数}
// C++20 解决方案:用 std::optional#include <optional>
void append_to_cpp20(int item, std::optional<std::vector<int>> target = std::nullopt) { std::vector<int> vec = target.value_or({}); // 创建新 vector vec.push_back(item); std::cout << "Result: " << vec.size() << std::endl;}
int main() { append_to_cpp20(1); // Result: 1 append_to_cpp20(2); // Result: 1(每次都是新 vector)}默认参数规则
Section titled “默认参数规则”// ✅ 正确:默认参数放在参数列表末尾void connect(const std::string& host, int port = 80, bool use_ssl = false);
// 允许的情况void f(int a, int b = 0, int c = 0); // OKvoid f(int a = 0); // OK
// ❌ 错误:默认参数不能在非默认参数前面void f(int a = 0, int b); // 错误void f(int a, int b = 0, int c); // 错误:c 在有默认值的 b 后没有默认值
// 正确示例void create_user(const std::string& name, const std::string& email = "", int age = 0, bool verified = false);默认参数的实现原理
Section titled “默认参数的实现原理”// 编译器如何处理默认参数// 源代码:void greet(const std::string& name, const std::string& greeting = "Hello") { std::cout << greeting << ", " << name << std::endl;}
greet("Alice"); // 编译器转换为 greet("Alice", "Hello")greet("Bob", "Hi"); // 保持原样
// 本质:默认参数是编译时展开,不是运行时处理重载和默认参数的冲突
Section titled “重载和默认参数的冲突”#include <iostream>
// 陷阱:重载导致二义性void process(int x);void process(int x, int y = 10); // 添加了默认参数
// 调用int main() { process(5); // ❌ 编译错误!两个函数都匹配 // process(int) 直接匹配 // process(int, int) 也能匹配,因为 y 有默认值
process(5, 3); // ✅ OK,调用第二个 return 0;}
// 解决方案 1:不要这样重载void process_single(int x) { /* ... */ }void process_double(int x, int y) { /* ... */ }
// 解决方案 2:用 nullptr 区分重载#include <cstddef>
void process(int x);void process(int x, int* y); // 第二个参数是指针
int main() { process(5); // 调用第一个 process(5, nullptr); // 调用第二个}默认参数的副作用
Section titled “默认参数的副作用”#include <iostream>#include <vector>
// 陷阱:默认参数是 static 或全局变量std::vector<int> results;
void add_item(int value, std::vector<int>& container = results) { container.push_back(value);}
int main() { add_item(1); // 添加到 results add_item(2); // 添加到 results(累积)
std::cout << results.size() << std::endl; // 2
// 清空 results.clear();
add_item(3); // results 又累积 std::cout << results.size() << std::endl; // 1}默认参数和成员函数
Section titled “默认参数和成员函数”#include <string>#include <vector>
class Database {private: std::string connection_string_; int timeout_;
public: // 成员函数的默认参数 void connect(const std::string& uri = "localhost:5432", int timeout = 30) { connection_string_ = uri; timeout_ = timeout; }
void query(const std::string& sql, bool async = false) { // ... }};
// 使用int main() { Database db; db.connect(); // 使用默认 uri 和 timeout db.connect("postgres://example.com"); // 使用自定义 uri db.connect("postgres://example.com", 60); // 自定义 uri 和 timeout
db.query("SELECT * FROM users"); // async = false db.query("SELECT * FROM users", true); // async = true}12.3 constexpr 函数
Section titled “12.3 constexpr 函数”编译期计算 vs 运行时计算
Section titled “编译期计算 vs 运行时计算”# Python - 无编译期计算概念def factorial(n): if n <= 1: return 1 return n * factorial(n)
# 全部在运行时计算result = factorial(10) # 运行时才计算// C++ constexpr - 编译期计算constexpr int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1);}
// 编译期验证!static_assert(factorial(5) == 120, "factorial(5) must be 120");
int main() { // 两种用法 int runtime_val = factorial(10); // 可能在编译期计算,也可能在运行时 constexpr int compile_val = factorial(10); // 强制编译期计算}constexpr 的发展历史
Section titled “constexpr 的发展历史”// C++11 - constexpr 限制很多constexpr int factorial(int n) { return (n <= 1) ? 1 : n * factorial(n - 1);}
// C++14 - 解锁更多特性(允许更复杂的函数体)constexpr int fibonacci(int n) { if (n <= 1) return n; int a = 0, b = 1; for (int i = 2; i <= n; ++i) { int temp = a + b; a = b; b = temp; } return b;}
// C++17 - 允许 if constexprtemplate<typename T>constexpr T abs(T x) { if constexpr (std::is_signed_v<T>) { return x < 0 ? -x : x; } else { return x; }}
// C++20 - constexpr 支持更多 STL 容器#include <vector>constexpr auto create_vector() { std::vector<int> v = {1, 2, 3}; return v; // C++20 开始支持}constexpr 使用场景
Section titled “constexpr 使用场景”#include <array>
// 1. 数组大小必须是编译期常量constexpr int SIZE = 10;int arr[SIZE]; // OK
// 2. 模板参数必须是编译期常量template<int N>struct ArrayHolder { int data[N];};
ArrayHolder<factorial(5)> holder; // 数组大小 120
// 3. static_assert 编译期断言static_assert(sizeof(int) >= 4, "int must be at least 32 bits");static_assert(factorial(6) == 720);
// 4. 查找表constexpr int triangle_number(int n) { return n * (n + 1) / 2;}
constexpr std::array<int, 10> triangles = { triangle_number(0), triangle_number(1), triangle_number(2), triangle_number(3), triangle_number(4), triangle_number(5), triangle_number(6), triangle_number(7), triangle_number(8), triangle_number(9)};constexpr vs inline
Section titled “constexpr vs inline”| 特性 | constexpr | inline |
|---|---|---|
| 目的 | 编译期求值 | 消除调用开销 |
| 适用 | 常量表达式 | 短小函数 |
| 保证 | 编译期计算 | 建议优化 |
| 要求 | 无副作用 | 短小函数体 |
// constexpr 隐含 inlineconstexpr int square(int x) { return x * x; } // 同时是 constexpr 和 inline
// 但两者目的不同constexpr int consteval_fn(int x) { return x + 1; } // 必须编译期求值inline int inline_fn(int x) { return x + 1; } // 建议内联consteval(C++20)
Section titled “consteval(C++20)”// consteval - 强制编译期求值consteval int factorial_consteval(int n) { if (n <= 1) return 1; return n * factorial_consteval(n - 1);}
int main() { constexpr int a = factorial_consteval(5); // OK,编译期 int b = factorial_consteval(5); // OK,编译期(强制)
int n = 5; // int c = factorial_consteval(n); // ❌ 错误!n 不是常量 // factorial_consteval 要求参数也是编译期可确定
int d = square(n); // OK!square 不是 consteval return 0;}
// constexpr 可以有时在编译期,有时在运行时constexpr int square(int x) { return x * x; }
int main() { constexpr int a = square(5); // 编译期 int n = 5; int b = square(n); // 运行时(n 不是常量)}12.4 内联函数
Section titled “12.4 内联函数”Python 函数调用开销
Section titled “Python 函数调用开销”# Python - 每个函数调用有开销import time
def max(a, b): return a if a > b else b
start = time.time()for i in range(10000000): m = max(i, 100)end = time.time()print(f"Time: {end - start:.3f}s") # 约 1-2 秒C++ inline
Section titled “C++ inline”// inline 建议编译器内联(消除函数调用开销)inline int max(int a, int b) { return a > b ? a : b;}
int main() { for (int i = 0; i < 10000000; ++i) { int m = max(i, 100); // 编译器可能展开为: int m = i > 100 ? i : 100; }}// 源代码inline int add(int a, int b) { return a + b;}
int main() { int result = add(1, 2);}
// 编译器可能转换为int main() { int result = 1 + 2; // 直接内联展开}内联适用场景
Section titled “内联适用场景”// ✅ 适合内联inline int square(int x) { return x * x; }inline int max(int a, int b) { return a > b ? a : b; }inline bool is_even(int x) { return x % 2 == 0; }
// ❌ 不适合内联inline int fibonacci(int n) { // 递归不适合 if (n <= 1) return n; return fibonacci(n-1) + fibonacci(n-2);}
inline void complex_operation(...) { // 复杂函数不适合 // 大量代码 // 循环 // 分支}inline 注意事项
Section titled “inline 注意事项”// 1. inline 必须在头文件中定义(因为需要编译时展开)// .h 文件inline int add(int a, int b) { return a + b; }
// 2. 模板函数隐式 inlinetemplate<typename T>T max(T a, T b) { // 不需要写 inline,编译器自动处理 return a > b ? a : b;}
// 3. 类内定义的成员函数隐式 inlinestruct Point { int x, y;
// 隐式 inline(在类内定义) int getX() const { return x; } int getY() const { return y; }};
// 4. 过大的内联会导致代码膨胀(bloat)inline void big_operation() { // 1000 行代码 // ...}内联 vs 宏
Section titled “内联 vs 宏”// C 宏(有问题)#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main() { int x = 5, y = 10; int m = MAX(x++, y++); // 陷阱!x 和 y 各被递增两次 // 展开为: ((x++) > (y++) ? (x++) : (y++)) // 结果不可预测}
// C++ 内联函数(安全)template<typename T>inline T max(T a, T b) { return a > b ? a : b;}
int main() { int x = 5, y = 10; int m = max(x++, y++); // OK,每个表达式只求值一次}12.5 函数指针
Section titled “12.5 函数指针”#include <iostream>
int add(int a, int b) { return a + b; }int multiply(int a, int b) { return a * b; }int subtract(int a, int b) { return a - b; }
int main() { // 声明函数指针 int (*operation)(int, int);
// 指向函数 operation = add; int result = operation(3, 4); // 7
// 重新指向 operation = multiply; result = operation(3, 4); // 12
// 也可以用 & 取地址(但不是必需的) operation = &subtract; result = operation(3, 4); // -1}typedef 和 using
Section titled “typedef 和 using”#include <iostream>
// 使用 typedeftypedef int (*BinaryOp)(int, int);using BinaryOp = int(*)(int, int); // C++11 using 方式更清晰
BinaryOp get_operation(char op) { switch (op) { case '+': return add; case '*': return multiply; case '-': return subtract; default: return nullptr; }}
int main() { if (auto op = get_operation('+')) { std::cout << op(5, 3) << std::endl; // 8 }}函数指针数组
Section titled “函数指针数组”#include <iostream>
int add(int a, int b) { return a + b; }int multiply(int a, int b) { return a * b; }int subtract(int a, int b) { return a - b; }int divide(int a, int b) { return b != 0 ? a / b : 0; }
int main() { // 函数指针数组 int (*operations[])(int, int) = {add, multiply, subtract, divide};
for (int i = 0; i < 4; ++i) { std::cout << operations[i](10, 3) << " "; // 13, 30, 7, 3 } std::cout << std::endl;}回调函数示例
Section titled “回调函数示例”#include <iostream>#include <vector>
// Python: def apply(data, callback): return [callback(x) for x in data]
// C++ 回调函数std::vector<int> apply(const std::vector<int>& data, int (*callback)(int)) { std::vector<int> result; result.reserve(data.size()); for (int x : data) { result.push_back(callback(x)); } return result;}
int double_it(int x) { return x * 2; }int square_it(int x) { return x * x; }
int main() { std::vector<int> nums = {1, 2, 3, 4, 5};
auto doubled = apply(nums, double_it); // doubled = {2, 4, 6, 8, 10}
auto squared = apply(nums, square_it); // squared = {1, 4, 9, 16, 25}
for (int n : doubled) std::cout << n << " "; std::cout << std::endl;}#include <iostream>#include <vector>#include <algorithm>
struct Person { std::string name; int age;};
bool compare_by_age(const Person& a, const Person& b) { return a.age < b.age;}
bool compare_by_name(const Person& a, const Person& b) { return a.name < b.name;}
int main() { std::vector<Person> people = { {"Alice", 30}, {"Bob", 25}, {"Charlie", 35} };
// 使用函数指针排序 std::sort(people.begin(), people.end(), compare_by_age); // 按 age 排序
std::sort(people.begin(), people.end(), compare_by_name); // 按 name 排序}12.6 std::function(更现代的方式)
Section titled “12.6 std::function(更现代的方式)”std::function 基础
Section titled “std::function 基础”#include <functional>#include <iostream>#include <vector>
int add(int a, int b) { return a + b; }int multiply(int a, int b) { return a * b; }
int main() { // std::function 可以存储任何可调用对象 std::function<int(int, int)> operation;
operation = add; // 函数指针 std::cout << operation(3, 4) << std::endl; // 7
operation = multiply; // 重新赋值 std::cout << operation(3, 4) << std::endl; // 12
// 存储 lambda operation = [](int a, int b) { return a + b; }; std::cout << operation(5, 6) << std::endl; // 11}std::function vs 函数指针
Section titled “std::function vs 函数指针”| 特性 | 函数指针 | std::function |
|---|---|---|
| 类型安全 | 编译期检查 | 编译期检查 |
| 内存开销 | 仅指针 | 有虚调用开销 |
| 可存储 | 仅函数 | 任何可调用对象 |
| 空状态 | 指向 nullptr | 可为 std::function<...>{} |
#include <functional>
// 函数指针版本(更轻量)template<typename T>T apply(T value, int (*fn)(T)) { // 只能接受函数指针 return fn(value);}
// std::function 版本(更灵活)template<typename T>T apply Flexible(T value, std::function<T(T)> fn) { // 可以接受任何可调用对象 return fn ? fn(value) : value;}
int double(int x) { return x * 2; }
int main() { apply(5, double); // OK apply(5, [](int x) { return x * 2; }); // ❌ 函数指针不能存储 lambda
// std::function 版本 apply_flexible(5, double); // OK apply_flexible(5, [](int x) { return x * 2; }); // OK}std::function 作为容器
Section titled “std::function 作为容器”#include <functional>#include <iostream>#include <vector>#include <map>
int main() { // 命令模式 std::map<std::string, std::function<void()>> commands;
commands["help"] = []() { std::cout << "Available commands\n"; }; commands["quit"] = []() { std::cout << "Goodbye!\n"; }; commands["version"] = []() { std::cout << "Version 1.0\n"; };
// 执行命令 std::string cmd = "help"; if (auto it = commands.find(cmd); it != commands.end()) { it->second(); // 调用函数 }
// 数字计算器 std::map<char, std::function<int(int, int)>> ops; ops['+'] = [](int a, int b) { return a + b; }; ops['-'] = [](int a, int b) { return a - b; }; ops['*'] = [](int a, int b) { return a * b; }; ops['/'] = [](int a, int b) { return b ? a / b : 0; };
std::cout << ops['+'](10, 3) << std::endl; // 13 std::cout << ops['*'](10, 3) << std::endl; // 30}成员函数和 std::bind
Section titled “成员函数和 std::bind”#include <functional>#include <iostream>#include <vector>
struct Calculator { int value = 0;
void add(int x) { value += x; } void multiply(int x) { value *= x; } void print() const { std::cout << value << std::endl; }};
int main() { Calculator calc;
// 使用 lambda 包装成员函数 auto add_fn = [&calc](int x) { calc.add(x); }; add_fn(10);
// 或者用 std::bind auto add_bound = std::bind(&Calculator::add, &calc, std::placeholders::_1); add_bound(5);
// 存储成员函数指针 std::vector<std::function<void(int)>> operations; operations.push_back([&calc](int x) { calc.add(x); }); operations.push_back([&calc](int x) { calc.multiply(x); });
for (auto& op : operations) { op(2); // 先加2,再乘2 }
calc.print(); // 24 ((0 + 10) + 5) * 2 * 2 = 60... wait // ((0 + 10) * 2) * 2 = 40}std::function 的开销
Section titled “std::function 的开销”#include <functional>#include <chrono>#include <iostream>
void simple_fn() { /* empty */ }
int main() { // 函数指针调用几乎零开销 void (*fn_ptr)() = simple_fn;
// std::function 有虚调用开销 std::function<void()> fn = simple_fn;
// 性能测试 const int iterations = 10000000;
auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { fn_ptr(); } auto end = std::chrono::high_resolution_clock::now(); std::cout << "Function pointer: " << std::chrono::duration<double>(end - start).count() << "s\n";
start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { fn(); } end = std::chrono::high_resolution_clock::now(); std::cout << "std::function: " << std::chrono::duration<double>(end - start).count() << "s\n";}12.7 完整示例:计算器
Section titled “12.7 完整示例:计算器”#include <iostream>#include <functional>#include <map>#include <string>#include <vector>#include <sstream>#include <cctype>
class Calculator {private: double memory_ = 0; std::map<char, std::function<double(double, double)>> operations_; std::map<char, std::function<void()>> commands_;
public: Calculator() { // 算术运算 operations_['+'] = [](double a, double b) { return a + b; }; operations_['-'] = [](double a, double b) { return a - b; }; operations_['*'] = [](double a, double b) { return a * b; }; operations_['/'] = [](double a, double b) { return b != 0 ? a / b : 0; }; operations_['%'] = [](double a, double b) { return static_cast<int>(a) % static_cast<int>(b); };
// 命令(使用 lambda) commands_['c'] = [this]() { memory_ = 0; }; commands_['q'] = [this]() { std::cout << "Bye!\n"; }; commands_['s'] = [this]() { std::cout << "Current: " << memory_ << "\n"; }; }
bool calculate(const std::string& expression) { std::istringstream iss(expression); double a, b; char op;
if (!(iss >> a >> op >> b)) { std::cerr << "Invalid format. Use: <num> <op> <num>\n"; return false; }
if (auto it = operations_.find(op); it != operations_.end()) { memory_ = it->second(a, b); std::cout << "= " << memory_ << "\n"; return true; }
std::cerr << "Unknown operator: " << op << "\n"; return false; }
void run_repl() { std::cout << "Calculator REPL (type 'q' to quit, 'c' to clear)\n";
while (true) { std::cout << "> "; std::string line; if (!std::getline(std::cin, line)) break; if (line.empty()) continue;
// 检查命令 if (line == "q" || line == "quit") break; if (line == "c" || line == "clear") { memory_ = 0; continue; } if (line == "s" || line == "status") { std::cout << "Memory: " << memory_ << "\n"; continue; }
calculate(line); } }};
int main() { Calculator calc; calc.run_repl(); return 0;}12.8 章节总结
Section titled “12.8 章节总结”| 概念 | 关键点 |
|---|---|
| 递归 | 与 Python 类似;C++ 不保证尾递归优化;注意栈溢出 |
| 默认参数 | 必须是常量表达式;在参数列表末尾;避免与重载混用 |
| constexpr | 编译期求值;C++14 支持复杂函数体;C++20 支持容器 |
| consteval | 强制编译期求值;参数必须也是常量 |
| inline | 消除调用开销;适合短小函数;必须在头文件定义 |
| 函数指针 | 低开销;typedef/using 简化;用于回调 |
| std::function | 统一接口;可存储任何可调用对象;有虚调用开销 |
下章预告:ch13 学习 Lambda 表达式和闭包,掌握 C++ 的匿名函数。