C++ 函数全解

news/2024/11/25 9:15:03/

引言

在C++编程中,函数是程序的基本构建块之一。函数可以将代码组织成可重用的模块,提高代码的可读性和可维护性。本文将详细介绍C++函数的各种特性,包括函数的定义、调用、参数传递、返回值、重载、内联函数、递归函数、函数指针、Lambda 函数、模板函数和函数对象。

1. 函数的定义

1.1 基本语法

函数的基本定义语法如下:

返回类型 函数名(参数列表) {// 函数体
}
  • 返回类型:函数执行后返回的数据类型。如果函数不返回任何值,使用 void
  • 函数名:函数的名称,用于调用函数。
  • 参数列表:函数接受的参数,可以为空。
  • 函数体:函数的具体实现。

1.2 示例

int add(int a, int b) {return a + b;
}

1.3 函数声明与定义

函数可以在声明后定义,也可以在定义时声明。声明通常放在头文件中,定义放在源文件中。

// 声明
int add(int a, int b);// 定义
int add(int a, int b) {return a + b;
}

2. 函数的调用

2.1 基本调用

函数调用时,需要提供与参数列表匹配的参数。

int result = add(3, 5); // result = 8

2.2 函数调用的执行过程

  1. 参数传递:将实际参数传递给函数的形参。
  2. 执行函数体:执行函数体中的代码。
  3. 返回结果:函数执行完毕后,返回结果(如果有)。

2.3 函数调用的栈帧

每次调用函数时,系统会在栈上为该函数分配一个栈帧,用于存储函数的局部变量和参数。函数调用结束后,栈帧被释放。

3. 参数传递

3.1 按值传递

按值传递是指将实际参数的值复制给形参。形参的任何修改都不会影响实际参数。

void modifyValue(int x) {x = 10;
}int main() {int a = 5;modifyValue(a);std::cout << a << std::endl; // 输出 5return 0;
}

3.2 按引用传递

按引用传递是指将实际参数的引用传递给形参。形参的任何修改都会影响实际参数。

void modifyValue(int& x) {x = 10;
}int main() {int a = 5;modifyValue(a);std::cout << a << std::endl; // 输出 10return 0;
}

3.3 按指针传递

按指针传递是指将实际参数的地址传递给形参。通过指针可以修改实际参数。

void modifyValue(int* x) {*x = 10;
}int main() {int a = 5;modifyValue(&a);std::cout << a << std::endl; // 输出 10return 0;
}

3.4 默认参数

默认参数是指在函数声明中为参数提供默认值。如果调用函数时没有提供相应的参数,将使用默认值。

void greet(std::string name, std::string greeting = "Hello") {std::cout << greeting << ", " << name << "!" << std::endl;
}int main() {greet("Alice"); // 输出 "Hello, Alice!"greet("Bob", "Hi"); // 输出 "Hi, Bob!"return 0;
}

3.5 可变参数

可变参数函数可以接受不同数量的参数。C++11 引入了 std::initializer_list 和变长模板来实现可变参数。

void printNumbers(std::initializer_list<int> numbers) {for (int num : numbers) {std::cout << num << " ";}std::cout << std::endl;
}int main() {printNumbers({1, 2, 3, 4, 5}); // 输出 "1 2 3 4 5 "return 0;
}

3.6 常量引用参数

常量引用参数可以防止函数修改传入的参数。

void print(const std::string& message) {std::cout << message << std::endl;
}int main() {std::string msg = "Hello, World!";print(msg); // 输出 "Hello, World!"return 0;
}

4. 返回值

4.1 基本返回值

函数可以返回一个值,返回值的类型在函数声明时指定。

int add(int a, int b) {return a + b;
}

4.2 多返回值

C++ 不直接支持多返回值,但可以通过返回结构体或元组来实现。

#include <tuple>std::tuple<int, int> addAndSubtract(int a, int b) {return std::make_tuple(a + b, a - b);
}int main() {int sum, diff;std::tie(sum, diff) = addAndSubtract(10, 5);std::cout << "Sum: " << sum << ", Difference: " << diff << std::endl; // 输出 "Sum: 15, Difference: 5"return 0;
}

4.3 返回引用

函数可以返回引用,允许调用者修改返回的值。

int& getMax(int& a, int& b) {return (a > b) ? a : b;
}int main() {int x = 10, y = 20;getMax(x, y) = 30;std::cout << "x: " << x << ", y: " << y << std::endl; // 输出 "x: 10, y: 30"return 0;
}

4.4 返回指针

函数可以返回指针,但需要注意指针的有效性。

int* createArray(int size) {int* arr = new int[size];for (int i = 0; i < size; ++i) {arr[i] = i;}return arr;
}int main() {int* arr = createArray(5);for (int i = 0; i < 5; ++i) {std::cout << arr[i] << " ";}delete[] arr; // 释放内存return 0;
}

5. 函数重载

5.1 基本概念

函数重载是指在同一个作用域中定义多个同名函数,但参数列表不同。编译器根据参数列表选择合适的函数。

void print(int x) {std::cout << "Integer: " << x << std::endl;
}void print(double x) {std::cout << "Double: " << x << std::endl;
}int main() {print(5); // 调用 print(int)print(5.0); // 调用 print(double)return 0;
}

5.2 重载规则

  • 参数列表必须不同。
  • 返回类型不同不能作为重载的依据。
  • 参数的常量性和引用性可以作为重载的依据。

5.3 重载示例

void print(const char* str) {std::cout << "String: " << str << std::endl;
}void print(const std::string& str) {std::cout << "String: " << str << std::endl;
}int main() {print("Hello"); // 调用 print(const char*)print(std::string("World")); // 调用 print(const std::string&)return 0;
}

6. 内联函数

6.1 基本概念

内联函数是一种优化手段,编译器会在调用点直接插入函数体,避免函数调用的开销。

inline int add(int a, int b) {return a + b;
}

6.2 使用场景

  • 函数体非常短小。
  • 调用频繁。

6.3 内联函数的限制

  • 内联函数的定义必须在所有调用点之前可见。
  • 过多的内联函数可能会增加编译时间和可执行文件的大小。

7. 递归函数

7.1 基本概念

递归函数是指在函数体中调用自身的函数。递归函数需要有一个终止条件,否则会导致无限递归。

int factorial(int n) {if (n == 0) {return 1;}return n * factorial(n - 1);
}int main() {std::cout << "Factorial of 5: " << factorial(5) << std::endl; // 输出 "Factorial of 5: 120"return 0;
}

7.2 递归优化

  • 尾递归:如果递归调用是函数的最后一个操作,编译器可以优化为循环,避免栈溢出。
int factorial(int n, int result = 1) {if (n == 0) {return result;}return factorial(n - 1, n * result);
}int main() {std::cout << "Factorial of 5: " << factorial(5) << std::endl; // 输出 "Factorial of 5: 120"return 0;
}

7.3 递归示例:斐波那契数列

int fibonacci(int n) {if (n <= 1) {return n;}return fibonacci(n - 1) + fibonacci(n - 2);
}int main() {std::cout << "Fibonacci of 5: " << fibonacci(5) << std::endl; // 输出 "Fibonacci of 5: 5"return 0;
}

8. 函数指针

8.1 基本概念

函数指针是指向函数的指针,可以用来调用函数。

int add(int a, int b) {return a + b;
}int main() {int (*func)(int, int) = add; // 声明并初始化函数指针int result = func(3, 5); // 调用函数std::cout << "Result: " << result << std::endl; // 输出 "Result: 8"return 0;
}

8.2 函数指针数组

可以使用函数指针数组来存储多个函数指针。

int add(int a, int b) {return a + b;
}int subtract(int a, int b) {return a - b;
}int main() {int (*funcArray[2])(int, int) = {add, subtract}; // 函数指针数组int result1 = funcArray[0](3, 5); // 调用 addint result2 = funcArray[1](3, 5); // 调用 subtractstd::cout << "Result1: " << result1 << ", Result2: " << result2 << std::endl; // 输出 "Result1: 8, Result2: -2"return 0;
}

8.3 函数指针作为参数

可以将函数指针作为参数传递给其他函数。

void applyFunction(int (*func)(int, int), int a, int b) {int result = func(a, b);std::cout << "Result: " << result << std::endl;
}int main() {applyFunction(add, 3, 5); // 输出 "Result: 8"applyFunction(subtract, 3, 5); // 输出 "Result: -2"return 0;
}

8.4 函数指针的类型别名

可以使用 typedef 或 using 关键字为函数指针创建类型别名。

typedef int (*FuncPtr)(int, int);void applyFunction(FuncPtr func, int a, int b) {int result = func(a, b);std::cout << "Result: " << result << std::endl;
}int main() {applyFunction(add, 3, 5); // 输出 "Result: 8"applyFunction(subtract, 3, 5); // 输出 "Result: -2"return 0;
}

9. Lambda 函数

9.1 基本概念

Lambda 函数是一种匿名函数,可以在需要函数的地方直接定义和使用。

int main() {auto add = [](int a, int b) -> int {return a + b;};int result = add(3, 5);std::cout << "Result: " << result << std::endl; // 输出 "Result: 8"return 0;
}

9.2 Lambda 的捕获列表

Lambda 函数可以捕获外部变量,使用捕获列表来指定捕获方式。

int main() {int x = 10;auto add = [x](int a) -> int {return a + x;};int result = add(5);std::cout << "Result: " << result << std::endl; // 输出 "Result: 15"return 0;
}

9.3 Lambda 作为参数

Lambda 函数可以作为参数传递给其他函数。

void applyFunction(std::function<int(int, int)> func, int a, int b) {int result = func(a, b);std::cout << "Result: " << result << std::endl;
}int main() {applyFunction([](int a, int b) -> int { return a + b; }, 3, 5); // 输出 "Result: 8"applyFunction([](int a, int b) -> int { return a - b; }, 3, 5); // 输出 "Result: -2"return 0;
}

9.4 Lambda 的捕获方式

  • 按值捕获[x],捕获 x 的值。
  • 按引用捕获[&x],捕获 x 的引用。
  • 捕获所有局部变量[=],按值捕获所有局部变量。
  • 捕获所有局部变量的引用[&],按引用捕获所有局部变量。

10. 模板函数

10.1 基本概念

模板函数可以处理不同类型的参数,提高代码的通用性。

template <typename T>
T add(T a, T b) {return a + b;
}int main() {int result1 = add(3, 5); // 输出 "Result1: 8"double result2 = add(3.0, 5.0); // 输出 "Result2: 8.0"std::cout << "Result1: " << result1 << ", Result2: " << result2 << std::endl;return 0;
}

10.2 模板特化

可以为特定类型提供模板特化,实现特定类型的优化。

template <>
int add<int>(int a, int b) {return a + b + 1; // 特化版本
}int main() {int result1 = add(3, 5); // 输出 "Result1: 9"double result2 = add(3.0, 5.0); // 输出 "Result2: 8.0"std::cout << "Result1: " << result1 << ", Result2: " << result2 << std::endl;return 0;
}

10.3 模板参数

模板参数可以是类型参数,也可以是非类型参数。

template <typename T, int N>
T multiply(T a) {return a * N;
}int main() {int result1 = multiply<int, 5>(3); // 输出 "Result1: 15"double result2 = multiply<double, 2>(3.0); // 输出 "Result2: 6.0"std::cout << "Result1: " << result1 << ", Result2: " << result2 << std::endl;return 0;
}

11. 函数对象

11.1 基本概念

函数对象是指重载了 operator() 的类对象,可以像函数一样调用。

class Add {
public:int operator()(int a, int b) {return a + b;}
};int main() {Add add;int result = add(3, 5);std::cout << "Result: " << result << std::endl; // 输出 "Result: 8"return 0;
}
``

11. 函数对象

11.2 函数对象的捕获

函数对象可以捕获外部变量,类似于Lambda函数。这可以通过在类中使用成员变量来实现。

class Add {
private:int base;
public:Add(int base) : base(base) {}int operator()(int a) {return a + base;}
};int main() {Add add(10);int result = add(5);std::cout << "Result: " << result << std::endl; // 输出 "Result: 15"return 0;
}
11.3 函数对象的灵活性

函数对象可以提供更多的灵活性,例如可以包含多个操作或状态。

class Calculator {
public:int add(int a, int b) {return a + b;}int subtract(int a, int b) {return a - b;}
};int main() {Calculator calc;int result1 = calc.add(3, 5);int result2 = calc.subtract(3, 5);std::cout << "Result1: " << result1 << ", Result2: " << result2 << std::endl; // 输出 "Result1: 8, Result2: -2"return 0;
}

12. 函数的高级用法

12.1 函数模板的默认参数

模板函数可以有默认参数,类似于普通函数。

template <typename T, int N = 2>
T multiply(T a) {return a * N;
}int main() {int result1 = multiply<int>(3); // 使用默认参数 N = 2int result2 = multiply<int, 5>(3); // 指定参数 N = 5std::cout << "Result1: " << result1 << ", Result2: " << result2 << std::endl; // 输出 "Result1: 6, Result2: 15"return 0;
}
12.2 函数模板的显式特化

可以为特定类型提供显式特化,实现特定类型的优化。

template <typename T>
T add(T a, T b) {return a + b;
}template <>
int add<int>(int a, int b) {return a + b + 1; // 特化版本
}int main() {int result1 = add(3, 5); // 输出 "Result1: 9"double result2 = add(3.0, 5.0); // 输出 "Result2: 8.0"std::cout << "Result1: " << result1 << ", Result2: " << result2 << std::endl;return 0;
}
12.3 函数模板的部分特化

部分特化允许为特定类型的一部分提供特化版本。

template <typename T, typename U>
struct Add {static T add(T a, U b) {return a + b;}
};template <typename T>
struct Add<T, int> {static T add(T a, int b) {return a + b + 1; // 特化版本}
};int main() {int result1 = Add<int, int>::add(3, 5); // 输出 "Result1: 9"double result2 = Add<double, double>::add(3.0, 5.0); // 输出 "Result2: 8.0"std::cout << "Result1: " << result1 << ", Result2: " << result2 << std::endl;return 0;
}

13. 函数的最佳实践

13.1 函数的命名

函数的命名应该清晰、简洁,能够反映其功能。使用动词或动词短语来命名函数。

void printMessage(const std::string& message) {std::cout << message << std::endl;
}
13.2 函数的长度

函数应该保持简短,通常不超过20行代码。如果函数过长,可以考虑将其拆分为多个函数。

void processRequest(const std::string& request) {parseRequest(request);validateRequest(request);executeRequest(request);
}void parseRequest(const std::string& request) {// 解析请求
}void validateRequest(const std::string& request) {// 验证请求
}void executeRequest(const std::string& request) {// 执行请求
}
13.3 函数的参数

尽量减少函数的参数数量。如果参数过多,可以考虑使用结构体或类来传递参数。

struct Request {std::string url;std::string method;std::map<std::string, std::string> headers;
};void processRequest(const Request& request) {// 处理请求
}
13.4 函数的返回值

函数的返回值应该明确,避免使用全局变量或外部状态来传递结果。

int add(int a, int b) {return a + b;
}
13.5 函数的错误处理

函数应该有明确的错误处理机制,可以使用异常、错误码或可选类型来处理错误。

int divide(int a, int b) {if (b == 0) {throw std::runtime_error("Division by zero");}return a / b;
}int main() {try {int result = divide(10, 0);std::cout << "Result: " << result << std::endl;} catch (const std::exception& e) {std::cout << "Error: " << e.what() << std::endl;}return 0;
}

14. 总结

C++函数是程序设计中的重要组成部分,通过合理地使用函数,可以提高代码的可读性、可维护性和可重用性。本文详细介绍了C++函数的各种特性,包括函数的定义、调用、参数传递、返回值、重载、内联函数、递归函数、函数指针、Lambda函数、模板函数和函数对象。希望本文能够帮助您更好地理解和使用C++函数。


http://www.ppmy.cn/news/1549778.html

相关文章

Unity中动态生成贴图并保存成png图片实现

实现原理&#xff1a; 要生成长x宽y的贴图&#xff0c;就是生成x*y个像素填充到贴图中&#xff0c;如下图&#xff1a; 如果要改变局部颜色&#xff0c;就是从x1到x2(x1<x2),y1到y2(y1<y2)这个范围做处理&#xff0c; 或者要想做圆形就是计算距某个点&#xff08;x1,y1&…

Vscode进行Java开发环境搭建

Vscode进行Java开发环境搭建 搭建Java开发环境(Windows)1.Jdk安装2.VsCode安装3.Java插件4.安装 Spring 插件5.安装 Mybatis 插件5.安装Maven环境6.Jrebel插件7.IntelliJ IDEA Keybindings8. 收尾 VS Code&#xff08;Visual Studio Code&#xff09;是由微软开发的一款免费、开…

【c++篇】:探秘红黑树平衡旋转原理--维持树的高度平衡之道

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;c篇–CSDN博客 文章目录 前言一.红黑树的概念和性质定义和性质节点结构与表示 二.模拟实现基…

七天掌握SQL--->第四天:事务处理与并发控制

# 7天掌握SQL - 第四天&#xff1a;事务处理与并发控制 ## 目标 - 学习事务处理的基本概念&#xff0c;如ACID特性。 - 掌握并发控制的方法&#xff0c;如锁机制、事务隔离级别等。 - 通过实际案例练习事务处理和并发控制。 ## 1. 事务处理的基本概念 事务处理是数据库管理系…

鸿蒙主流路由详解

鸿蒙主流路由详解 Navigation Navigation更适合于一次开发,多端部署,也是官方主流推荐的一种路由控制方式,但是,使用起来入侵耦合度高,所以,一般会使用HMRouter,这也是官方主流推荐的路由 Navigation官网地址 个人源码地址 路由跳转 第一步-定义路由栈 Provide(PageInfo) pag…

idea添加版权信息

1、添加Copyright Profiles 打开Settings -> Editor -> Copyright -> Copyright Profiles -> 新增 Copyright (c) 【你的版权信息】 【开始年份】-${today.year}. All rights reserved.如&#xff1a; Copyright (c) by cwp 2024-${today.year}. All rights rese…

Zero-Shot Next-Item Recommendation using Large Pretrained Language Models-问题咨询

问题背景&#xff1a; 我正在尝试复现一篇名为 “Zero-Shot Next-Item Recommendation using Large Pretrained Language Models” 的论文中的实验结果。 遇到的问题&#xff1a; 缺少关键代码&#xff1a; 我发现论文代码库中没有提供计算 HR10 和 NDCG10 这两个关键指标的…

JavaWeb后端开发知识储备2

目录 1.HttpClient 2.微信小程序开发 3.Spring Cache 1.HttpClient 简单来说&#xff0c;HttpClient可以通过编码的方式在Java中发送Http请求 2.微信小程序开发 微信小程序的开发本质上是前端开发&#xff0c;对于后端程序员来说了解即可 3.Spring Cache Spring Cache 是…