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::pairreturn {42, 3.14};标准库支持只能两个值
std::tuplereturn make_tuple(1, 2.0, "3");任意数量值访问不便(需get
输出参数void getBoth(int& a, double& b);高效接口不直观
std::arrayreturn 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 性能优化

  1. 小对象(<=寄存器大小):直接返回值
  2. 大对象:依赖RVO或返回智能指针
  3. 频繁调用的热路径函数:考虑返回引用/指针

3.5.4 API设计原则

  1. 一致性:相似函数使用相同返回模式
  2. 可预测性:调用者能明确知道是否获得所有权
  3. 文档化:清楚说明返回值的生命周期和所有权

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 Tclass 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. 最佳实践

  1. 单一职责原则:每个函数只做一件事
  2. 合理长度:通常不超过一屏幕
  3. 命名规范:动词开头,描述功能
  4. 参数数量:建议不超过4个
  5. 错误处理
    - 使用异常处理不可恢复错误
    - 使用返回码处理可恢复错误
  6. const正确性
    - 不修改参数的函数声明为const
    - 成员函数不修改对象状态声明为const
  7. 避免全局函数:优先使用命名空间组织函数

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 }
文章目录