c++复习自存--函数

📅 2026/7/3 11:24:59 👁️ 阅读次数 📝 编程学习
c++复习自存--函数

使用函数处理不同类型的数据

一、函数重载

1. 完整定义

同一个作用域(全局/同一个类)中,存在多个函数名完全相同,但形参列表存在差异的一组函数,编译器会根据调用时传入实参的个数、类型、顺序自动匹配对应函数,这个机制称为函数重载。

2. 构成重载的3种合法差异
  1. 参数个数不同:void f(int);/void f(int, double);
  2. 参数类型不同:void f(int);/void f(double);
  3. 参数顺序不同:void f(int,char);/void f(char,int);
3. ❌ 不能作为重载区分的条件(高频错题)
  1. 仅返回值不同:int f();/double f();不算重载,编译报错;
  2. 仅形参变量名不同:void f(int a);/void f(int b);完全等价,无重载;
  3. 仅顶层const修饰值参数:void f(int);/void f(const int);视为同一个函数。
4. 匹配优先级(编译器调用逻辑)

1.精准匹配(类型完全一致)→ 2.平凡隐式转换(int→long)→ 3.提升转换(char→int)→ 4.算术转换(int→double)

若多个函数同时满足转换条件,产生调用二义性,直接编译失败。

5. 代码示例
#include<iostream>usingnamespacestd;// 三组重载print函数voidprint(intx){cout<<"整数:"<<x<<endl;}voidprint(doublex){cout<<"浮点数:"<<x<<endl;}voidprint(inta,string b){cout<<a<<" "<<b<<endl;}intmain(){print(10);// 匹配int版本print(3.14);// 匹配double版本print(20,"test");// 匹配双参数版本return0;}
6. 适用场景

统一功能逻辑,适配多种数据类型,无需给不同类型函数起不同名字,提升代码可读性。


二、将数组传递给函数

1. 底层本质

数组名作为函数实参时,隐式转换为数组首元素指针,函数内部不会拷贝整个数组,只传递一个地址,因此:

  1. 函数内修改数组元素,会直接修改外部原数组;
  2. 函数内部sizeof(arr)获取的是指针大小,而非数组总字节长度,必须手动传入数组长度参数。
2. 三种等价传参写法(编译器全部解析为指针)
// 写法1:数组形式(可读性最高,竞赛常用)voidfunc(intarr[],intlen);// 写法2:指定数组长度(数字会被编译器忽略,无实际约束)voidfunc(intarr[100],intlen);// 写法3:原生指针形式,和上面完全等价voidfunc(int*arr,intlen);
3. 二维数组传参强制规则

多维数组传参时,除第一维外,其余维度大小必须显式指定

voidmat(intarr[][5],introw);// 合法,第二维固定5列// void mat(int arr[][], int row); // 非法,无法计算元素偏移
4. const数组传参优化

只读数组建议加const,禁止函数内部修改数组,同时兼容常量数组实参:

voidshow(constintarr[],intlen);
5. 易错坑点

定长数组int a[10]、动态数组vector<int>、指针int* p都可以传给数组形参,但长度必须手动传递;std::array不属于原生数组,传参规则不同。


三、按引用传递参数

1. 引用底层原理

引用T&变量别名,编译期直接绑定实参内存地址,运行时不产生任何副本,不分配额外栈内存。
语法:void func(int& x),调用时func(num)x等价于num

2. 两大核心作用
  1. 减少拷贝开销:传递stringvector、结构体、大数组等大型对象,避免完整复制;
  2. 函数内修改外部变量:无需通过返回值带出修改结果,直接操作原变量。
3. 常量引用const T&(最优只读传参方案)
voidprintVec(constvector<int>&v);

优势:

  • 不拷贝,性能和原生指针一致;
  • 编译器禁止函数内部修改容器,保证数据安全;
  • 可接收字面量、临时对象作为实参(普通左值引用T&不支持临时量)。
4. 值传递 / 左值引用 / const引用 完整对比
传递方式语法是否拷贝能否修改外部变量能否接收临时值
值传递void f(int x)拷贝副本不能可以
普通引用void f(int& x)无拷贝可以不可以
const引用void f(const int& x)无拷贝不可以可以
什么是「临时值(临时对象)」

程序运行中没有独立变量名、转瞬即逝、用完立刻销毁的数值/对象,就叫临时值。
常见例子:

  1. 字面常量:103.14"abc"
  2. 表达式运算结果:a + bx * 2
  3. 函数返回值:add(1,2)vec.back()
  4. 构造临时对象:vector<int>{1,2,3}

临时值存放在寄存器/临时内存,没有左值身份,不能取地址、不能被普通左值引用绑定

  1. 值传递void f(int x):能接收临时值

调用示例:

voidf(intx){}f(10);// 字面临时值 OKf(2+3);// 表达式临时结果 OK

原理:
临时值会拷贝一份副本给形参x,函数操作副本,和原临时对象无关,所以允许传入临时。

  1. 普通左值引用void f(int& x):不能接收临时值
voidf(int&x){}f(10);// 编译报错!f(1+2);// 编译报错!

原因:
int& x要求绑定有名字、可修改的持久变量(左值)
临时值是转瞬即逝的右值,编译器禁止普通引用绑定它——如果允许绑定,你在函数内修改x,但临时对象马上销毁,修改毫无意义,C++直接禁止这种危险行为。

合法调用只能传已有变量:

inta=5;f(a);// 只有带名字的变量能传给int&
  1. const引用void f(const int& x):可以接收临时值
voidf(constint&x){}f(10);// OKf(3*4);// OK

原理:
const T&常量左值引用,编译器做了特殊规则放宽:
允许绑定临时对象,并且会延长临时对象的生命周期,直到函数执行完毕再销毁。
同时加了const限制:函数内不能修改这个临时值,不存在“修改无效”的隐患,所以语法放行。


微处理器如何处理函数调用

一、 内联函数 inline

1. CPU普通函数调用底层开销

调用普通函数时,CPU需要:保存现场寄存器→开辟栈帧→参数压栈→跳转执行函数→返回恢复寄存器,频繁小函数调用会产生大量性能损耗。

2. inline核心作用

inline修饰函数,向编译器提交建议:将函数体代码直接嵌入每一处调用位置,消除函数跳转、栈帧开辟的开销。

3. 适用场景

函数体短小(1~5行)、逻辑简单、高频循环调用:数值计算、取值判断、简单存取函数。

4. ❌ 禁止使用inline的场景

包含循环、递归、大量分支判断、复杂IO操作的函数;编译器会自动忽略inline建议,降级为普通函数。

5. 特殊规则
  1. inline只是建议,不是强制命令,编译器拥有最终决定权;
  2. 类内部直接定义的成员函数,会被编译器隐式标记为inline;
  3. inline函数定义必须和声明放在同一头文件(多文件共用)。
6. 示例
inlineintsquare(intx){returnx*x;}intmain(){inta=square(5);// 编译后直接替换为 int a = 5*5;return0;}

二、自动推断返回类型

分为两套语法,适配C++11/C++14标准:

1. C++14 简化auto返回推断(最常用)
autoadd(inta,intb){returna+b;// 编译器根据return表达式自动推导返回值类型}

约束:函数所有return语句必须返回完全相同类型,否则编译报错;无法用于递归函数(编译器无法提前推导类型)。

2. C++11 尾置返回类型decltype(auto)

解决参数为引用、返回值需要保留引用属性的场景:

// 保留变量引用属性autogetVal(int&x)->decltype(x){returnx;}

适用场景:模板函数、返回类型依赖参数类型的泛型代码。

(1)普通auto返回的缺陷
autogetVal(int&x){returnx;}inta=10;autores=getVal(a);res=99;

这里res普通int副本,不是引用:
auto推导时会剥离引用属性,就算返回的是引用变量x,返回值依然是值拷贝,无法修改外部a

(2)decltype(表达式)作用

decltype(变量/表达式)原样获取表达式的完整类型,不会剥离引用、const修饰。

intnum=5;int&r=num;decltype(r)x=num;// x的类型就是 int&,保留引用
(3)尾置返回语法auto 函数(参数) -> 类型

C++11 标准写法:
auto只是占位符,真正返回类型写在->后面;
好处:返回类型里可以直接使用函数的参数名(普通前置返回做不到)。

// auto:占位符,真正返回类型在 -> 后指定// -> decltype(x):返回类型 = x 的完整原始类型(int&)autogetVal(int&x)->decltype(x){returnx;}
  1. 形参int& xx是传入实参的左值引用,类型为int&
  2. decltype(x):提取x完整类型,结果就是int&
  3. -> decltype(x):强制指定函数返回值类型为int&,保留引用属性

补充:decltype(auto)简化版(C++14)

C++14新增decltype(auto),不需要尾置语法,等价简化:

// C++14 一行替代尾置写法,自动保留引用/constdecltype(auto)getVal(int&x){returnx;}
  • auto:丢弃引用、const
  • decltype(auto):完整保留返回表达式的所有类型修饰(引用、const、volatile)

三、lambda匿名函数

Lambda =匿名函数:没有函数名字、可以就地临时写出来、用完就丢的小型函数。
普通函数需要单独定义在全局 / 类里,而 Lambda 可以直接写在调用它的地方(比如 sort、循环、回调里),专门处理一次性的简短逻辑。

1. 完整标准语法
[捕获列表](形参列表)mutable->返回类型{函数体;}

各分段作用:

  1. []捕获列表:控制lambda访问外部局部变量的方式;
  2. ()参数列表:和普通函数参数规则一致;
  3. mutable可选:允许修改值捕获的外部变量;
  4. ->类型尾置返回:可省略,编译器自动推断返回类型。
2. 捕获规则(考试核心)
捕获写法含义
[=]值捕获:复制所有外部局部变量,只读
[&]引用捕获:绑定所有外部变量别名,可修改原变量
[x]仅值捕获变量x
[&x]仅引用捕获变量x
[=, &y]默认全部值捕获,唯独y使用引用捕获
[this]捕获当前类对象指针(类内lambda专用)
3. 经典实战场景(STL排序)
#include<algorithm>#include<vector>usingnamespacestd;intmain(){vector<int>v={3,1,4,2};// lambda作为排序比较函数,降序排列sort(v.begin(),v.end(),[](inta,intb){returna>b;});return0;}
4. mutable关键字说明

值捕获的变量默认const只读,添加mutable后可在lambda内修改副本,但不会影响外部原变量:

intx=10;autof=[=]()mutable{x++;};f();// 仅修改lambda内部副本,外部x仍为10
5. 优势

无需单独定义全局/局部函数,临时逻辑就地编写,大幅简化算法回调、异步回调代码。