计算机
c base
c++ 基础 作用域及生命周期
c++ template
c++ 内存视角
c++ 函数
c++ 基础 基础语法
c++ 性能
c++ 类 基础
c++ 类 对象模型 类析构
c++ 类 设计模式
C++ STL
cmake
CMAKE环境搭建 windows
创建第一个cmakelists.txt
构建稍复杂的项目
动态链接库
EX1
EX1 START
EX1 ANSWER
EX2
EX2 START
EX2 ANSWER
EX3
EX3 START
EX3 ANSWER
变量
控制流程
函数和宏
查找和使用外部库
生成器表达式
qt
qt c++
qt index
qt qml quick
qt ui
qt 多线程
理解QObject 1
理解QObject 4
C++ 技巧 反射
理解QObject 2
理解QObject 3
理解QObject 6
理解 QObject 5
QCoreApplication
QApplication
数据结构
PC问题监控及排查
PC程序性能优化
OS
TOOL
编程漫谈
sealos+frp,搭建内网穿透
host主机配置
C++实战 主题
多线程
生产者-消费者流水线
IO
网络
Bilinotes搭建
编译原理
WEB开发
TS
HTML CSS JAVASCRIPT
本站点使用 MrDoc 构建
-
+
c++ 函数
"发布于 2025-02-25 13:44" ref<c++ primer plus> # quick use ``` void func(int,int){ } void func(&a,&b){ } void func(_a,_b){ } ``` # 函数 (函数原型、函数调用) 原型描述了函数到编译器的接口 函数原型不需要提供变量名,有类型列表就够了 。圆形中的变量名相当于占位符,所以不必与函数定义中的变量名相同。 void cheers(int); ### 声明和定义的区别 不要将函数定义或者变量声明放到头文件 例如,如果在头文件包含一个函数定义,然后再其他两个文佳佳你(属于同一个程序)中包含该头文件,则同一个程序中将包含同一个函数的两个定义 | 特性 | 声明 | 定义 | |------|------|------| | 格式 | `类型 函数名(参数);` | `类型 函数名(参数) { 函数体 }` | | 函数体 | ❌ 没有 | ✅ 必须有 | | 内存分配 | ❌ 不分配 | ✅ 分配代码空间 | | 可以重复 | ✅ 可以多次声明 | ❌ 只能定义一次(ODR规则) | | 必须出现 | 调用前 | 链接前 | 源文件结构: ``` header.h (声明) ↓ #include main.cpp (使用函数) math.cpp (定义函数) ↓ 编译 ↓ 编译 main.o (有外部引用) math.o (有函数代码) ↓ ↓ └───────→ 链接器 ←────────┘ ↓ 可执行文件 ``` ### 按值传递 (函数参数、按值传递) 将数值参数传递给函数,后者将其赋给一个新的变量 ### 初始化 auto auto:自动类型推导 ## 数组 (函数和指针,const,) C++将数组名解释为其第一个元素地址    ### 指针和数组对比 | 操作 | 数组 `int arr[5]` | 指针 `int* ptr` | |------|------------------|-----------------| | `sizeof` | 20(整个数组大小) | 8(指针大小) | | `&` 取地址 | `int(*)[5]`(指向数组) | `int**`(指向指针) | | 可以赋值 | ❌ 不能 | ✅ 可以 | | 下标访问 | ✅ `arr[2]` | ✅ `ptr[2]` | | 指针运算 | ✅ `arr+2` | ✅ `ptr+2` | | 作为参数 | 退化为指针 | 保持指针 | ### const void show_array(const double ar[], int n) void show_array(double ar[], int n) //接收数组名的函数将使用原始数据,需加const进行保护 //需要修改则不保护 arr[20] etc 禁止将 const 的地址赋给非 const 指针,这意味着可以修改const指向的值 const + pointer 涉及一级间接关系,将非const指针赋值给const指针可以 使用const的好处: 1 可以避免无意间的数据修改导致的编程错误 2 const使得函数能处理const和非const实参 int * const ptr = &a //无法修改指针的值 const int * ptr = &a //无法修改ptr指向的值 void show_array(const double arr[],int n){} //无法修改传入的参数 ### 二维数组 data[3][4],下面是两种函数参数传递方式: int sum(int(*ar2)[][4]) int sum(int ar2[][4],int size) ## 字符串 字符串形式: 1 char数组 2 引号括起的字符串常量 3 被设置为字符串地址的char指针 C-风格字符串 vs 常规char数组:字符串有内置结束字符 ## 结构 按值传递、按地址传递、按引用传递 引用 底层来看是 const ptr;使用来看是地址别名 ```cpp int* ptr = &a; // 普通指针 *ptr = 30; // 通过指针修改 a 的值 ``` ## 递归 结构递归 和 生成递归 | 特性 | 结构递归 (Structural Recursion) | 生成递归 (Generative Recursion) | |------|-------------------------------|-------------------------------| | 核心逻辑 | 跟随数据结构走 (Follow the data) | 拆解问题/生成数据 (Divide & conquer) | | 输入变化 | 输入通常缩小(如移动到 next 节点) | 输入转化为新的、通常更小的子问题 | | 风险 | 主要风险是栈溢出 | 除了栈溢出,还有指数级爆炸 (如 fib) | | 优化方案 | 尾递归优化 (Tail recursion) | 记忆化 (Memoization) 或转迭代 | | 经典例子 | 遍历链表、遍历树、文件系统遍历 | 快速排序、斐波那契、阿克曼函数 | ## 指针 使用函数名(后面不跟参数),就能获取函数的地址 函数指针:声明的函数指针要像函数原型一样指出有关函数的信息 声明应指定函数的返回类型以及函数的特征标(参数列表)。 使用:使用(*pf)时,只要将其看成函数名即可 ## 内联函数 内联函数:编译器使用相应的函数代码替换函数调用,能提高运行效率,但整体来看,作用不大 inline 函数<声明>和<定义>前加 inline 内联函数不能递归 内联函数和常规函数一样,也是按值传递参数,如果参数为表达式,内联函数将传递表达式的值 C define宏通过文本替换实现 ## 变量引用 引用变量主要用途是用作函数的形参 记住:必须在声明引用时进行初始化 引用更接近const指针,必须在创建时初始化,一旦与某个变量关联,就将一致效忠于它//引用不能修改指针的值,但能修改值 int * const pr = &rats; ```cpp int rats; cint & rodents = rata //int &是指向int的引用 cout << &rodents << endl //&是地址操作符,表示rondents引用变量的地址 ``` 当数据量比较大时,引用参数将很有用 如果实参与引用参数不匹配,c++将生成临时变量 临时变量只在函数调用期存在, 如果声明将引用指定为const,c++将在必要的时候生成临时变量 如果引用参数是const,则编译器将在下面两种情况下生成临时变量: 1 实参类型正确,但不是左值 2 实参类型不正确,转换成正确的类型 派生类继承了基类的方法 基类可以指向派生类对象,而无需进行强制类型转换。这种特征的一个实际结果是,可以定义一个接收基类引用作为参数的函数 ## 默认参数 vs 函数重载 必须通过函数原型设置默认值 仅当函数基本上执行相同的任务,但使用不同形式的数据时,才应该采用函数重载 特征标,而不是函数类型使得可以对函数进行重载 如果函数需要不同类型的参数时,默认参数就不管用了,这时候应该使用函数重载 | 特性 | 默认参数 | 函数重载 | |------|----------|----------| | 参数数量 | 固定,部分可省略 | 可以完全不同 | | 参数类型 | 固定 | 可以不同 | | 函数体数量 | 1个 | 多个 | | 代码重复 | 无 | 可能有重复逻辑 | | 适用场景 | 参数有合理默认值 | 不同参数需要不同处理 | ## 函数模板 模板允许以通用类型的方式编写程序,这种类型是用参数表示对额,因此模板特性有时被称为参数化类型(parameterized types) template 具体化机制: 如果有多个原型,编译器在选择原型时,非模板版本将优先显式具体化和模板版本;显式具体化将优先于使用模板生成的版本 实例化: 模板并非函数定义,但是使用int的模板实例是函数定义,这叫隐式实例化 隐式实例化、显式实例化和显式具体化统称为具体化,表示都i是使用具体类型的函数定义 template <> swap(int &, int &); //显式具体化 编译器选择函数版本策略: 1 创建候选函数列表 2 使用候选函数列表创建可行函数列表 3 确定是否有最佳可行函数 # lambda函数 捕获、泛型lambda、可变lambda ## 捕获 ``` int x = 10, y = 20; // 值捕获 auto lambda1 = [x, y]() { return x + y; }; // 引用捕获 auto lambda2 = [&x, &y]() { x++; y++; }; // 隐式捕获 auto lambda3 = [=]() { return x + y; }; // 全部值捕获 auto lambda4 = [&]() { x++; y++; }; // 全部引用捕获 // 混合捕获 auto lambda5 = [=, &y]() { y++; return x; }; // x值捕获,y引用捕获 auto lambda6 = [&, x]() { x++; y++; }; // x值捕获(const),y引用捕获 // C++14:初始化捕获(移动语义) auto ptr = std::make_unique<int>(10); auto lambda7 = [ptr = std::move(ptr)]() { return *ptr; }; ``` ## 泛型 ``` // 泛型lambda auto add = [](auto a, auto b) { return a + b; }; int i = add(10, 20); // 实例化为int版本 double d = add(3.14, 2.86); // 实例化为double版本 string s = add(string("Hello"), string(" World")); // 带模板语法的泛型lambda(C++20) auto compare = []<typename T>(T a, T b) { return a > b; }; // 完美转发 auto wrapper = [](auto&&... args) { return std::forward<decltype(args)>(args)...; }; ``` ## 可变lambda ``` // 错误:不能修改值捕获的变量 int counter = 0; auto bad = [counter]() { counter++; // 编译错误:counter是const的 }; // 使用mutable修饰符 int counter = 0; auto good = [counter]() mutable { counter++; // 允许修改副本 return counter; }; cout << good(); // 输出1 cout << good(); // 输出2 cout << counter; // 仍然输出0(原始变量未变) ``` # 函数使用技巧 | 技巧 | 主要用途 | 使用频率 | |------|----------|----------| | 函数指针 | 回调、策略模式 | ⭐⭐⭐ | | inline | 小函数优化 | ⭐⭐⭐⭐ | | 尾置返回类型 | 复杂返回类型 | ⭐⭐ | | decltype | 类型推导 | ⭐⭐⭐ | | lambda | 局部函数、算法 | ⭐⭐⭐⭐⭐ | | std::function | 存储任意可调用对象 | ⭐⭐⭐⭐ | | 默认参数 | 简化调用 | ⭐⭐⭐⭐⭐ | | 可变参数模板 | 泛型库实现 | ⭐⭐ | | constexpr | 编译期计算 | ⭐⭐⭐ |
peipeo
2026年5月21日 14:03
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码