当前位置: 首页 > news >正文

【C++ 从基础到项目实战】C++(三):函数进阶——重载、回调、递归与默认参数

📌 阅读时长:25分钟 | 关键词:C++、函数重载、默认参数、内联函数、回调函数、递归调用、函数指针

引言

函数是 C++ 代码复用的基本单元。但仅仅会定义和调用函数远远不够——真正的生产力来自函数的高级用法:怎么让同一个函数适应不同类型?怎么把函数当作参数传来传去?怎么优雅地处理重复操作?这篇文章一次讲透。

一、参数传递的三种方式

在进入函数高级特性之前,先夯实参数传递的基础——这直接影响程序的正确性和效率。

// 1. 值传递:函数内修改不影响实参voidbyValue(inta,intb){inttemp=a;a=b;b=temp;}// 2. 指针传递:通过地址修改实参voidbyPointer(int*a,int*b){inttemp=*a;*a=*b;*b=temp;}// 3. 引用传递:通过别名修改实参,语法最简洁voidbyReference(int&a,int&b){inttemp=a;a=b;b=temp;}intmain(){intx=10,y=20;byValue(x,y);// x=10, y=20 未变byPointer(&x,&y);// x=20, y=10 已交换byReference(x,y);// x=10, y=20 又换回来}
传参方式能否修改实参效率推荐场景
值传递低(需拷贝)简单类型、不需修改
指针传递动态内存、可能为 nullptr
引用传递高(无拷贝)需要修改、语法简洁
常量引用const T&大对象只读传递(最佳实践)
// 常量引用:大对象只读传递的最佳选择voidprintInfo(conststd::string&name){std::cout<<"Name: "<<name<<std::endl;// name[0] = 'X'; // ❌ 错误,const 保护}

二、函数重载:同名不同参

C++ 允许同一个函数名拥有多个版本,只要参数列表不同(类型、数量或顺序):

intsum(inta,intb){// 两个 intreturna+b;}intsum(inta,intb,intc){// 三个 intreturna+b+c;}doublesum(doublea,doubleb){// 两个 doublereturna+b;}intmain(){std::cout<<sum(2,3)<<std::endl;// 5(自动匹配第一个)std::cout<<sum(2,3,4)<<std::endl;// 9(匹配第二个)std::cout<<sum(2.5,3.5)<<std::endl;// 6.0(匹配第三个)}

⚠️ 重载只看参数列表,不看返回值类型。int f()double f()不是重载,是编译错误。

三、默认参数:让函数调用更简洁

右向左给参数设默认值,调用时可省略:

// 计算矩形面积,width 默认为 1intarea(intlength,intwidth=1){returnlength*width;}// 打印消息,times 默认为 1voidprint(conststd::string&msg,inttimes=1){for(inti=0;i<times;++i)std::cout<<msg<<std::endl;}intmain(){std::cout<<area(5)<<std::endl;// 5*1 = 5std::cout<<area(5,3)<<std::endl;// 5*3 = 15print("Hello");// 打印 1 次print("World",3);// 打印 3 次}
规则说明
从右向左如果 a 有默认值,它右边的 b、c 也必须有
声明时指定默认值在函数声明中写一次即可
不重复指定声明和定义不能同时写默认值
调用时省略只能从右端开始省略参数

四、内联函数:牺牲体积换速度

inline建议编译器将函数体直接嵌入调用处,省去函数调用的压栈、跳转开销:

inlineintmax(inta,intb){returna>b?a:b;}inlinedoublesquare(doublex){returnx*x;}intmain(){intm=max(10,20);// 可能被替换成 20 > 10 ? 20 : 10doubles=square(3.5);// 可能被替换成 3.5 * 3.5}
适合内联不适合内联
短小函数(<10行)包含循环
频繁调用包含递归
getter/setter函数体过大
简单计算switch/goto 多分支

💡inline只是建议,编译器可能忽略。通常声明和定义放同一个头文件里。

五、函数指针:把函数当参数传递

函数指针存储函数的入口地址,可以把函数像数据一样传递:

#include<iostream>intadd(inta,intb){returna+b;}intsubtract(inta,intb){returna-b;}// 函数指针作为参数intoperate(inta,intb,int(*op)(int,int)){returnop(a,b);}intmain(){int(*funcPtr)(int,int);// 声明函数指针funcPtr=add;// 指向 addstd::cout<<funcPtr(5,3)<<std::endl;// 8std::cout<<operate(10,5,add)<<std::endl;// 15std::cout<<operate(10,5,subtract)<<std::endl;// 5}

函数指针的声明格式:返回值类型 (*指针名)(参数类型列表)

六、回调函数:让函数"通知"你

回调就是把函数指针传给另一个函数,让它在合适的时机反过来调用你:

#include<vector>#include<algorithm>boolascending(inta,intb){returna<b;}booldescending(inta,intb){returna>b;}voidsortNumbers(std::vector<int>&nums,bool(*cmp)(int,int)){std::sort(nums.begin(),nums.end(),cmp);// cmp 就是回调}intmain(){std::vector<int>v={3,1,4,1,5};sortNumbers(v,ascending);// 升序:1 1 3 4 5sortNumbers(v,descending);// 降序:5 4 3 1 1}

回调的典型应用场景:

场景示例
自定义排序规则std::sort的比较函数
事件处理GUI 按钮点击回调
异步通知网络请求完成后的回调
过滤条件std::copy_if的谓词函数

七、递归:函数调用自己

递归 =终止条件+递推关系。就像两面镜子互相对照:

// 阶乘:n! = n × (n-1)!intfactorial(intn){if(n==0)return1;// 终止条件returnn*factorial(n-1);// 递推关系}// 斐波那契:f(n) = f(n-1) + f(n-2)intfibonacci(intn){if(n<=1)returnn;returnfibonacci(n-1)+fibonacci(n-2);}intmain(){std::cout<<factorial(5)<<std::endl;// 120std::cout<<fibonacci(10)<<std::endl;// 55}

递归 vs 迭代

递归迭代
优点代码简洁、思路清晰效率高、内存占用小
缺点深度过大→栈溢出,重复计算某些问题实现复杂
选择树、图遍历、分治算法简单循环、追求性能

⚠️ 务必要有终止条件,否则无限递归导致程序崩溃!尾递归可以被编译器优化,但不依赖于此。

八、指针函数:返回指针的函数

函数可以返回指针,常用于返回动态分配的内存或查找结果:

int*createArray(intsize){int*arr=newint[size];// 堆上分配for(inti=0;i<size;++i)arr[i]=i*2;returnarr;// 返回堆内存地址(安全)}// ❌ 危险!不要返回局部变量的地址int*badFunction(){intlocal=10;return&local;// local 在函数返回后已销毁!}intmain(){int*arr=createArray(5);for(inti=0;i<5;++i)std::cout<<arr[i]<<" ";// 0 2 4 6 8delete[]arr;// 必须释放arr=nullptr;}

小结

序号知识点一句话总结
1参数传递值传递安全但慢,引用传递高效简洁,const引用最常用
2函数重载同名函数通过不同参数列表区分
3默认参数从右向左设默认值,让调用更简洁
4内联函数把函数体嵌入调用处,换取速度
5函数指针存储函数入口地址,让函数像数据一样传递
6回调函数把函数指针作参数,实现"通知"模式
7递归函数调用自己,必须有终止条件
8指针函数返回指针(通常是堆内存),注意不要返回局部变量地址

下一篇文章,我们将深入C++ 内存管理的核心——new/delete、栈与堆、全局/局部/静态变量,这是 C++ 开发中最容易踩坑的地方。


本文是「C++ 从基础到项目实战」系列的第 3 篇。关注我,不错过后续更新。

http://www.cnnetsun.cn/news/2723442.html

相关文章:

  • PL-2303驱动救赎记:让Windows 10与老芯片重归于好
  • 从‘删库跑路’到精准操作:手把手教你用jQuery的DOM方法(append, remove, empty)玩转动态网页
  • AI 视频智能体是什么?一文看懂「爆款仿剪→AI 生成→多平台发布」全流程
  • 保姆级教程:手把手教你从中国移动云盘下载并安装Matlab 2023b(附文件安装密钥)
  • 2026.05 english
  • 告别Clion?在VS2022里用Resharper C++插件实现智能重构与代码补全(附1.1版激活指南)
  • 从CHI ‘09到现代产品:人性化计算的核心框架与工程实践
  • FPGA新手避坑指南:用Vivado的Clocking Wizard搞定Xilinx 7系列时钟网络(附监控与抖动优化技巧)
  • AI图像描述如何满足视障用户多场景需求:从情境化设计到技术实现
  • UE5蓝图实战:用样条线做个能多次测量、一键清除的3D测距工具
  • 基于边缘计算的智慧停车场AI算力评估与SE110S-WA32部署方案
  • 数据密集型科学:从工具库到云平台,构建规模化研究的技术栈
  • HarmonyOS 6.1 开发者盛宴|《灵犀厨房》实战(二十三):【交互动效】转场、列表动画与趣味反馈——让每一次点击都有温度
  • 告别官方数据集:手把手教你将 YOLO 格式数据适配 SuperYOLO 训练流程
  • 从误报到修复:实战复盘一次AppScan扫描引发的‘虚惊一场’与优化配置
  • 别只知道UDP Flood了:2026年黑客最爱用的4种新型DDoS手法
  • DamaiHelper:告别黄牛票!Python自动化大麦网抢票脚本终极指南
  • 谱算符演算:解耦复杂系统交互,揭示经典谱理论盲区
  • MATLAB小波图像去噪工具包:含BaysShrink、Chang等自适应阈值算法及测试图与评估脚本
  • 11_Java集合框架概述
  • HoloLens混合现实应用开发实战:从工业设计到远程协作的四大核心场景
  • AI产品设计:从可用到好用的系统性设计思维与实践
  • 全栈开发硬核命题,拒绝CRUD男孩
  • UE5 VR开发避坑指南:Interaction组件里的Component Identification到底怎么用?
  • 类别不平衡问题
  • SNAP 9.0处理Sentinel-1 SLC数据:一个简化流程的避坑实践(跳过Split/Merge)
  • Redis中间件综合技术分析
  • 保姆级避坑指南:手把手教你用mmWave Studio 2.0搞定AWR1843雷达数据采集(从接线到.bin文件生成)
  • 配置存储卷
  • 别再只会用默认字符集了!Kali Linux中crunch的-f参数实战:调用内置字符库生成高命中率字典