Ch 11: 函数基础
- 理解函数的基本结构
- 掌握参数传递方式(值传递、引用传递、指针传递)
- 学会返回值的处理
- 理解函数重载和默认参数
11.1 Python vs C++ 函数
Section titled “11.1 Python vs C++ 函数”Python 函数回顾
Section titled “Python 函数回顾”# Python - 动态类型,灵活def add(a, b): return a + b
def greet(name, greeting="Hello"): return f"{greeting}, {name}!"
def process(data, callback): return callback(data)
# Python 允许任意类型result = add(1, 2) # 3result = add("hello", " world") # "hello world"result = add([1], [2, 3]) # [1, 2, 3]C++ 函数
Section titled “C++ 函数”// C++ - 静态类型,明确int add(int a, int b) { return a + b;}
// 默认参数std::string greet(const std::string& name, const std::string& greeting = "Hello") { return greeting + ", " + name + "!";}
// 函数重载 - 同名不同参数int add(int a, int b) { return a + b; }double add(double a, double b) { return a + b; }11.2 函数声明和定义
Section titled “11.2 函数声明和定义”// 声明(告诉编译器函数存在)int add(int a, int b);
// 定义(函数本体)int add(int a, int b) { return a + b;}
// 声明和定义可以合并int multiply(int a, int b) { return a * b;}C++11 后置返回类型
Section titled “C++11 后置返回类型”// 传统写法int add(int a, int b) { return a + b;}
// C++11 尾随返回类型auto add(int a, int b) -> int { return a + b;}
// 模板函数常用这种写法template<typename T, typename U>auto add(T a, U b) -> decltype(a + b) { return a + b;}函数声明的作用
Section titled “函数声明的作用”#ifndef UTILS_H#define UTILS_H
// 只声明int add(int a, int b);double square(double x);void greet(const std::string& name);
#endif
// utils.cpp#include "utils.h"
// 只定义int add(int a, int b) { return a + b;}11.3 参数传递
Section titled “11.3 参数传递”Python 参数传递回顾
Section titled “Python 参数传递回顾”# Python - 一切皆对象,按引用传递(不可变对象除外)def modify(x): x.append(100) # 修改原列表
def reassign(x): x = [1, 2, 3] # 重新绑定局部变量
lst = [1, 2]modify(lst) # lst 变为 [1, 2, 100]reassign(lst) # lst 仍是 [1, 2, 100]C++ 参数传递方式
Section titled “C++ 参数传递方式”#include <vector>
// 1. 值传递(拷贝)void by_value(int x) { x = 100; // 只修改副本,不影响原变量}
int a = 42;by_value(a);std::cout << a << std::endl; // 42
// 2. 引用传递void by_reference(int& x) { x = 100; // 修改原变量}
int b = 42;by_reference(b);std::cout << b << std::endl; // 100
// 3. 常量引用传递(推荐用于大对象)void by_const_ref(const std::vector<int>& v) { // v.push_back(1); // 错误!不能修改 std::cout << v.size() << std::endl; // 可以读取}
std::vector<int> vec = {1, 2, 3};by_const_ref(vec); // 不会拷贝 vector
// 4. 指针传递(类似 C)void by_pointer(int* ptr) { if (ptr) { // 检查空指针 *ptr = 100; // 解引用修改 }}
int c = 42;by_pointer(&c); // 传地址std::cout << c << std::endl; // 100什么时候用什么
Section titled “什么时候用什么”| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 小类型(int, double) | 值传递 | 拷贝成本低 |
| 大类型(vector, string) | 常量引用 | 避免拷贝,只读访问 |
| 需要修改大对象 | 引用传递 | 避免拷贝 |
| 输出参数(返回多个值) | 引用传递 | 比指针更清晰 |
| 可选参数(可能为空) | 指针 | 明确表示可选 |
#include <vector>#include <string>
// 小类型 - 值传递int calculate(int x, int y) { return x + y;}
// 大对象 - 常量引用double average(const std::vector<double>& values) { double sum = 0; for (double v : values) { sum += v; } return values.empty() ? 0 : sum / values.size();}
// 需要修改 - 引用void normalize(std::vector<double>& values) { double avg = average(values); for (double& v : values) { v -= avg; }}
// 输出参数 - 引用bool parse_int(const std::string& s, int& result) { try { result = std::stoi(s); return true; } catch (...) { return false; }}11.4 返回值
Section titled “11.4 返回值”// 基本类型返回值int add(int a, int b) { return a + b;}
// 返回大对象 - 使用移动语义std::vector<int> create_vector() { std::vector<int> v(1000); // ... 填充 v return v; // 移动,而不是拷贝(C++17 保证)}
// C++17 返回值直接构造std::pair<int, std::string> get_data() { return {42, "hello"}; // 直接构造}返回引用的问题
Section titled “返回引用的问题”// ❌ 危险:返回局部变量引用std::string& bad_reference() { std::string local = "hello"; return local; // 局部变量已销毁!}
// ✅ 正确:返回引用std::string& get_global_string() { static std::string s = "hello"; // static 存活到程序结束 return s;}
// ✅ 正确:返回值std::string get_string() { std::string s = "hello"; return s; // 返回值,调用者获得拷贝}返回指针 vs 返回引用
Section titled “返回指针 vs 返回引用”#include <memory>
// 返回指针 - 表示"可能为空"int* find_in_array(int* arr, size_t size, int target) { for (size_t i = 0; i < size; ++i) { if (arr[i] == target) { return &arr[i]; } } return nullptr; // 没找到}
// C++ 智能指针 - 现代方式std::unique_ptr<Node> create_node() { return std::make_unique<Node>();}11.5 函数重载
Section titled “11.5 函数重载”Python 不支持(用默认参数模拟)
Section titled “Python 不支持(用默认参数模拟)”# Python - 用默认参数模拟def process(data, verbose=False): if verbose: print(data) return data
def process(data): # 不能同名 return process(data, False)C++ 真重载
Section titled “C++ 真重载”// 同一个名字,不同参数void print(int x) { std::cout << "int: " << x << std::endl;}
void print(double x) { std::cout << "double: " << x << std::endl;}
void print(const std::string& s) { std::cout << "string: " << s << std::endl;}
int main() { print(42); // 调用 print(int) print(3.14); // 调用 print(double) print("hello"); // 调用 print(const char*) print(std::string("world")); // 显式构造}void f(int);void f(double);void f(int, int);void f(const char*);
f(1); // f(int)f(1.0); // f(double)f(1, 2); // f(int, int)f("text"); // f(const char*)
// 类型转换导致二义性// f(1.0f); // 错误!float 可以转 int 也可以转 double重载和const
Section titled “重载和const”// const 参数可以重载void process(int x);void process(const int x); // ❌ 错误:不能基于 const 重载
void process(int* ptr);void process(const int* ptr); // ✅ 可以:基于指针指向的对象的 const
void process(int& ref);void process(const int& ref); // ✅ 可以:基于引用的 const11.6 默认参数
Section titled “11.6 默认参数”Python 默认参数
Section titled “Python 默认参数”def greet(name, greeting="Hello"): return f"{greeting}, {name}!"
greet("Alice") # "Hello, Alice!"greet("Bob", "Hi") # "Hi, Bob!"C++ 默认参数
Section titled “C++ 默认参数”// C++ - 默认参数std::string greet(const std::string& name, const std::string& greeting = "Hello") { return greeting + ", " + name + "!";}
greet("Alice"); // "Hello, Alice!"greet("Bob", "Hi"); // "Hi, Bob!"默认参数规则
Section titled “默认参数规则”// ✅ 正确:默认参数放在参数列表末尾void f(int a, int b = 0, int c = 0);void f(int a = 0); // OK
// ❌ 错误void f(int a = 0, int b, int c); // 默认参数不能在非默认参数前面
// 正确示例void connect(const std::string& host, int port = 80, bool use_ssl = false, int timeout = 30);默认参数 vs 重载
Section titled “默认参数 vs 重载”// 陷阱:重载导致二义性void f(int x);void f(int x, int y = 10); // 可能导致二义性
f(5); // 哪个?编译器可能报错f(5, 3); // OK,明确调用第二个11.7 inline 函数
Section titled “11.7 inline 函数”# Python - 每个函数调用有开销def max(a, b): return a if a > b else b
for i in range(1000000): m = max(i, 100) # 每次都要创建栈帧// inline 建议编译器内联inline int max(int a, int b) { return a > b ? a : b;}
int main() { for (int i = 0; i < 1000000; ++i) { int m = max(i, 100); // 可能展开为:int m = i > 100 ? i : 100; }}内联适用场景
Section titled “内联适用场景”- 函数体小(几行代码)
- 频繁调用(循环内)
- 不包含复杂控制流(无递归)
- 不包含 goto
// ✅ 适合内联inline double square(double x) { return x * x; }inline int max(int a, int b) { return a > b ? a : b; }
// ❌ 不适合内联inline int fibonacci(int n) { // 递归,不适合 return n <= 1 ? n : fibonacci(n-1) + fibonacci(n-2);}11.8 完整示例
Section titled “11.8 完整示例”#include <iostream>#include <vector>#include <string>#include <optional>
// 参数传递:值传递(小类型)int square(int x) { return x * x;}
// 参数传递:常量引用(大类型)double average(const std::vector<double>& values) { if (values.empty()) return 0.0; double sum = 0; for (double v : values) { sum += v; } return sum / values.size();}
// 默认参数std::string format_name(const std::string& first, const std::string& last, const std::string& title = "") { if (title.empty()) { return first + " " + last; } return title + " " + first + " " + last;}
// 函数重载void print(int x) { std::cout << "int: " << x << std::endl;}
void print(double x) { std::cout << "double: " << x << std::endl;}
void print(const std::string& s) { std::cout << "string: " << s << std::endl;}
// 返回多个值std::optional<std::pair<int, int>> find_two_sum( const std::vector<int>& nums, int target) {
for (size_t i = 0; i < nums.size(); ++i) { for (size_t j = i + 1; j < nums.size(); ++j) { if (nums[i] + nums[j] == target) { return {{static_cast<int>(i), static_cast<int>(j)}}; } } } return std::nullopt;}
int main() { // 值传递 std::cout << square(5) << std::endl; // 25
// 常量引用 std::vector<double> vals = {1.0, 2.0, 3.0}; std::cout << average(vals) << std::endl; // 2.0
// 默认参数 std::cout << format_name("John", "Doe") << std::endl; std::cout << format_name("John", "Doe", "Dr.") << std::endl;
// 函数重载 print(42); print(3.14); print("hello");
// 查找 std::vector<int> nums = {2, 7, 11, 15}; if (auto result = find_two_sum(nums, 9)) { std::cout << "Found: " << result->first << ", " << result->second << std::endl; }
return 0;}- 函数需要声明返回类型
- 参数传递:值传递(拷贝)、引用传递(
&)、常量引用(const &) - 返回局部变量值,返回引用要小心
- C++ 支持函数重载(同名字不同参数)
- 默认参数放参数列表末尾
inline建议编译器内联,消除调用开销
下章预告:ch12 学习递归、内联、默认参数陷阱等高级函数话题。