017 C++的函数(Function)
作者Lou Xiao, deepseek创建时间2025-04-02 14:16:16更新时间2025-04-02 14:16:16
1. 函数基础概念
1.1 函数定义
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
return_type function_name(parameter_list) {
2
// 函数体
3
return value; // 如果return_type不是void
4
}
1.2 函数组成部分
- 返回类型:函数返回值的类型,void表示无返回值
- 函数名:标识函数的名称
- 参数列表:传递给函数的参数,可以为空
- 函数体:包含执行语句的代码块
1.3 函数声明与定义
声明(原型):告诉编译器函数的存在
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
return_type function_name(parameter_types);
定义:提供函数的实际实现
2. 函数参数
2.1 参数传递方式
传递方式 | 语法示例 | 特点 |
---|---|---|
值传递 | void func(int x) | 创建参数副本,不影响原值 |
引用传递 | void func(int &x) | 直接操作原变量 |
指针传递 | void func(int *x) | 通过地址操作原变量 |
const引用 | void func(const int &x) | 只读访问,避免拷贝开销 |
2.2 默认参数
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
void print(int value, int base = 10) {
2
// 如果调用时只提供一个参数,base默认为10
3
}
- 规则:
- 默认参数必须从右向左连续设置
- 默认参数通常在声明中指定,定义中不再重复
2.3 函数重载
- 同一作用域内,函数名相同但参数列表不同
- 区分标准:
- 参数类型不同
- 参数数量不同
- 参数顺序不同(不推荐)
- 不区分标准:
- 返回类型不同
- 参数名不同
3. 函数返回值
3.1. 返回值基础
3.1.1 基本语法
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
ReturnType functionName(Parameters) {
2
// 函数体
3
return expression; // 返回类型必须与ReturnType匹配或可隐式转换
4
}
3.1.2 返回值类型分类
类型 | 示例 | 特点 |
---|---|---|
基本类型 | int , double 等 | 直接返回值 |
复合类型 | 结构体、类对象 | 可能涉及拷贝构造 |
指针 | int* , MyClass* | 返回地址,需注意生命周期 |
引用 | int& , MyClass& | 返回别名,需注意生命周期 |
void | 无返回值 | 可单独使用return; 提前退出 |
3.2. 返回值传递机制
3.2.1 值返回(最常见)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
std::string getGreeting() {
2
return "Hello, World!"; // 返回临时对象
3
}
- 行为:创建返回值的副本(可能触发拷贝构造函数)
- 优化:编译器通常会使用返回值优化(RVO/NRVO)避免不必要的拷贝
3.2.2 引用返回
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
int& getElement(std::vector<int>& vec, size_t index) {
2
return vec[index]; // 返回现存对象的引用
3
}
- 适用场景:
- 返回类成员或参数中已存在的对象
- 实现链式调用(如
cout << a << b
)
- 注意事项:
- 绝对不要返回局部变量的引用
- 返回的引用生命周期应长于函数调用
3.2.3 指针返回
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
int* findValue(std::vector<int>& vec, int value) {
2
for (auto& item : vec) {
3
if (item == value) return &item;
4
}
5
return nullptr; // 未找到返回空指针
6
}
最佳实践:
- 明确文档说明调用者是否需要释放内存
- 优先使用智能指针(unique_ptr
, shared_ptr
)
- 对于"未找到"情况,返回nullptr
比抛出异常更合适
3.3. 现代C++返回值特性
3.3.1 返回值优化(RVO/NRVO)
RVO (Return Value Optimization)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
BigObject create() {
2
return BigObject(); // 编译器可能直接在调用处构造对象
3
}
NRVO (Named Return Value Optimization)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
BigObject create() {
2
BigObject obj;
3
// 对obj进行操作...
4
return obj; // 编译器可能避免拷贝
5
}
强制优化(C++17起):
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
BigObject create() {
2
BigObject obj;
3
return obj; // C++17起保证不拷贝
4
}
3.3.2 结构化绑定(C++17)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
std::tuple<int, double, std::string> getData() {
2
return {42, 3.14, "hello"};
3
}
4
5
auto [id, value, name] = getData(); // 分解返回值
3.3.3 返回类型推导(C++14)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
auto deduceReturnType(int x) {
2
if (x > 0) return 3.14; // double
3
return 42; // int → 编译错误(类型不一致)
4
}
5
6
auto lambda = [](auto x) { return x * 2; }; // 泛型lambda
3.4. 返回多个值的方法对比
方法 | 示例 | 优点 | 缺点 |
---|---|---|---|
结构体 | struct Result { int a; double b; }; | 类型安全,可扩展 | 需要额外定义 |
std::pair | return {42, 3.14}; | 标准库支持 | 只能两个值 |
std::tuple | return make_tuple(1, 2.0, "3"); | 任意数量值 | 访问不便(需get |
输出参数 | void getBoth(int& a, double& b); | 高效 | 接口不直观 |
std::array | return array<int,3>{1,2,3}; | 固定大小 | 同类型元素 |
3.5. 返回值设计最佳实践
3.5.1 错误处理模式
异常机制(推荐):
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
std::string loadFile(const std::string& path) {
2
if (!fileExists(path))
3
throw std::runtime_error("File not found");
4
// ...
5
}
返回错误码:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
std::pair<bool, std::string> tryLoadFile(const std::string& path) {
2
if (!fileExists(path))
3
return {false, ""};
4
return {true, fileContent};
5
}
optional/variant(C++17):
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
std::optional<std::string> safeLoadFile(const std::string& path) {
2
if (!fileExists(path))
3
return std::nullopt;
4
return fileContent;
5
}
3.5.2 生命周期管理
- 返回新对象:优先返回值(依赖RVO)
- 返回现有对象:返回引用/指针,但需文档说明所有权
- 返回资源:使用智能指针
cpp std::unique_ptr<Resource> createResource() { return std::make_unique<Resource>(); }
3.5.3 性能优化
- 小对象(<=寄存器大小):直接返回值
- 大对象:依赖RVO或返回智能指针
- 频繁调用的热路径函数:考虑返回引用/指针
3.5.4 API设计原则
- 一致性:相似函数使用相同返回模式
- 可预测性:调用者能明确知道是否获得所有权
- 文档化:清楚说明返回值的生命周期和所有权
3.6. 特殊案例与陷阱
3.6.1 返回局部变量
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
// 错误示例
2
const std::string& getName() {
3
std::string name = "Alice";
4
return name; // 返回局部变量的引用→未定义行为
5
}
6
7
// 正确做法(返回值)
8
std::string getName() {
9
return "Alice"; // 返回值优化生效
10
}
3.6.2 返回右值引用
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
// 移动语义示例
2
std::vector<int> createBigVector() {
3
std::vector<int> v(1000000);
4
return v; // 即使没有std::move也会优先移动
5
}
6
7
auto&& rvalueRefReturn() {
8
return std::move(createBigVector()); // 显式返回右值引用
9
}
3.6.3 返回函数指针
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
using Comparator = bool(*)(int, int);
2
3
Comparator getComparator(bool ascending) {
4
return ascending ?
5
[](int a, int b) { return a < b; } :
6
[](int a, int b) { return a > b; };
7
}
3.7. C++20新增特性
3.7.1 协程返回值
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
generator<int> range(int start, int end) {
2
for (int i = start; i < end; ++i)
3
co_yield i; // 每次调用返回一个值
4
}
5
6
// 使用
7
for (int i : range(1, 10)) {
8
std::cout << i << " ";
9
}
3.7.2 概念约束返回类型
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
auto add(std::integral auto a, std::integral auto b) {
2
return a + b; // 返回类型自动推导但受概念约束
3
}
4. 特殊函数类型
4.1 内联函数
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
inline int max(int a, int b) {
2
return a > b ? a : b;
3
}
- 特点:
- 编译器将函数体插入到调用处
- 减少函数调用开销
- 适合小型、频繁调用的函数
- 与宏的区别:类型安全,有作用域
4.2 递归函数
- 函数直接或间接调用自身
- 必须包含终止条件
- 示例:阶乘计算
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
int factorial(int n) {
2
if (n <= 1) return 1;
3
return n * factorial(n - 1);
4
}
4.3 constexpr函数(C++11)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
constexpr int square(int x) {
2
return x * x;
3
}
- 特点:
- 编译时求值
- 函数体通常只有return语句
- 参数和返回类型必须是字面类型
4.4 Lambda表达式(C++11)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
auto lambda = [capture](parameters) -> return_type {
2
// 函数体
3
};
- 捕获方式:
[]
:不捕获任何变量[=]
:值捕获所有变量[&]
:引用捕获所有变量[x, &y]
:混合捕获
5. 函数指针与函数对象
5.1 函数指针
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
int (*funcPtr)(int, int) = nullptr; // 声明
2
funcPtr = &add; // 赋值
3
int result = funcPtr(3, 4); // 调用
- 用途:回调函数、策略模式等
5.2 std::function(C++11)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
#include <functional>
2
std::function<int(int, int)> funcObj = add;
- 可以包装:
- 函数指针
- Lambda表达式
- 函数对象
- 成员函数(需使用std::bind)
5.3 函数对象(仿函数)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
struct Adder {
2
int operator()(int a, int b) const {
3
return a + b;
4
}
5
};
6
Adder addObj;
7
int sum = addObj(3, 4);
- 优点:
- 可以携带状态
- 比函数指针更高效(通常可内联)
6. 模板函数
6.1 函数模板
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
template <typename T>
2
T max(T a, T b) {
3
return a > b ? a : b;
4
}
- 实例化:
- 显式:
max<int>(3, 4)
- 隐式:
max(3, 4)
6.2 模板参数
- 类型参数:
typename T
或class T
- 非类型参数:
cpp template <int N> void print() { cout << N; }
- 模板模板参数:
cpp template <template <typename> class Container> void process(Container<int>& c) {}
6.3 可变参数模板(C++11)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
template <typename... Args>
2
void print(Args... args) {
3
// 使用折叠表达式或递归展开参数包
4
}
7. 异常处理
7.1 异常声明
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
void func() throw(); // C++98风格(已弃用)
2
void func() noexcept; // C++11风格
7.2 异常处理机制
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
try {
2
// 可能抛出异常的代码
3
throw std::runtime_error("Error message");
4
}
5
catch (const std::exception& e) {
6
// 处理异常
7
std::cerr << e.what();
8
}
9
catch (...) {
10
// 捕获所有异常
11
}
8. 最佳实践
- 单一职责原则:每个函数只做一件事
- 合理长度:通常不超过一屏幕
- 命名规范:动词开头,描述功能
- 参数数量:建议不超过4个
- 错误处理:
- 使用异常处理不可恢复错误
- 使用返回码处理可恢复错误 - const正确性:
- 不修改参数的函数声明为const
- 成员函数不修改对象状态声明为const - 避免全局函数:优先使用命名空间组织函数
9. C++17/20新增特性
9.1 if constexpr(C++17)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
template <typename T>
2
auto print(T value) {
3
if constexpr (std::is_integral_v<T>) {
4
cout << "Integer: " << value;
5
} else {
6
cout << "Other: " << value;
7
}
8
}
9.2 结构化绑定(C++17)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
auto [min, max] = minmax({3, 1, 4, 2});
9.3 概念约束(C++20)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
template <typename T>
2
requires std::integral<T>
3
T square(T x) { return x * x; }
9.4 协程(C++20)
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
generator<int> range(int start, int end) {
2
for (int i = start; i < end; ++i)
3
co_yield i;
4
}
文章目录