Skip to content

Ch 11: 函数基础

  • 理解函数的基本结构
  • 掌握参数传递方式(值传递、引用传递、指针传递)
  • 学会返回值的处理
  • 理解函数重载和默认参数
# 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) # 3
result = add("hello", " world") # "hello world"
result = add([1], [2, 3]) # [1, 2, 3]
// 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; }
// 声明(告诉编译器函数存在)
int add(int a, int b);
// 定义(函数本体)
int add(int a, int b) {
return a + b;
}
// 声明和定义可以合并
int multiply(int a, int b) {
return a * b;
}
// 传统写法
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;
}
utils.h
#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;
}
# 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]
#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
场景推荐方式原因
小类型(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;
}
}
// 基本类型返回值
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"}; // 直接构造
}
// ❌ 危险:返回局部变量引用
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; // 返回值,调用者获得拷贝
}
#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>();
}

Python 不支持(用默认参数模拟)

Section titled “Python 不支持(用默认参数模拟)”
# Python - 用默认参数模拟
def process(data, verbose=False):
if verbose:
print(data)
return data
def process(data): # 不能同名
return process(data, False)
// 同一个名字,不同参数
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 参数可以重载
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); // ✅ 可以:基于引用的 const
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
greet("Alice") # "Hello, Alice!"
greet("Bob", "Hi") # "Hi, Bob!"
// 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!"
// ✅ 正确:默认参数放在参数列表末尾
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);
// 陷阱:重载导致二义性
void f(int x);
void f(int x, int y = 10); // 可能导致二义性
f(5); // 哪个?编译器可能报错
f(5, 3); // OK,明确调用第二个
# 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;
}
}
  • 函数体小(几行代码)
  • 频繁调用(循环内)
  • 不包含复杂控制流(无递归)
  • 不包含 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);
}
#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 学习递归、内联、默认参数陷阱等高级函数话题。