第六章:函数基础——让代码学会“分工合作”

写代码写到一定规模,你就会发现:重复的代码段越来越多。复制粘贴虽然爽,但改起来真要命。这时候,函数就是你的救星。

函数说白了,就是把一段逻辑打包起来,给它起个名字,需要的时候叫它一声就行。我刚开始学C++时,觉得函数可有可无,直到有一次写了个两千行的main函数……嗯,从那以后我再也不敢不拆函数了。

核心概念速览:函数 = 返回值类型 + 函数名 + 参数列表 + 函数体。就这么简单。

6.1 函数的定义与声明

先看一个最基础的例子:

// 函数定义
int add(int a, int b) {
    return a + b;
}

// 函数声明(也叫函数原型)
int add(int a, int b);

定义和声明有什么区别?我个人习惯这样理解:声明是告诉编译器“有这个函数”,定义是告诉编译器“这个函数具体干什么”

你想想看,编译器读代码是从上往下的。如果函数定义写在调用之后,编译器就不认识它。这时候就需要提前声明一下。

我的习惯:头文件里放声明,源文件里放定义。这样别人看你的头文件,一眼就知道这个模块提供了哪些功能。

6.2 参数传递:值传递 vs 引用传递

这是新手最容易踩坑的地方。我见过太多人在这里翻车了。

6.2.1 值传递

void swap_value(int x, int y) {
    int temp = x;
    x = y;
    y = temp;
    // 这里交换的是副本,外面的变量纹丝不动
}

值传递,说白了就是把实参复制一份给形参。函数里改的是副本,原件不受影响。就像你复印了一份文件,在复印件上涂涂画画,原件还是干干净净的。

6.2.2 引用传递

void swap_ref(int &x, int &y) {
    int temp = x;
    x = y;
    y = temp;
    // 这里直接操作原件
}

引用传递就不一样了。它传的是实参的“别名”,函数里改的就是实参本身。我曾经在项目里遇到一个bug,排查了半天,最后发现是值传递导致数据没更新——嗯,从那以后我写交换函数必用引用。

传递方式 是否复制 能否修改实参 性能
值传递 大对象慢
引用传递
const引用传递 否(只读) 快且安全

避坑指南:如果你只是想读取数据,不想修改它,请用 const &。我曾经见过有人把大对象按值传递,结果程序跑起来又慢又占内存……

6.3 返回值

函数可以返回一个值,也可以什么都不返回(void)。

// 返回一个值
int max(int a, int b) {
    return (a > b) ? a : b;
}

// 不返回值
void print_hello() {
    std::cout << "Hello!" << std::endl;
    // 没有 return 语句,或者 return; 直接结束
}

这里有个细节:返回局部变量的引用是危险的。为什么?因为局部变量在函数结束时就销毁了,你拿到的引用指向一块已经被释放的内存。这叫“悬空引用”,是未定义行为。

经验之谈:返回大对象时,现代C++编译器有返回值优化(RVO),不用太担心性能。但如果你非要返回一个容器,可以考虑传引用参数来填充数据。

6.4 函数重载

C++允许你定义多个同名函数,只要它们的参数列表不同就行。这叫函数重载。

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

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

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

编译器会根据你传入的参数类型和个数,自动匹配最合适的版本。说白了,就是“同名不同参,各干各的活”。

但要注意:返回值类型不能作为重载的依据。下面这样写会编译报错:

int get_value();
double get_value();  // 错误!只有返回值不同

为什么会这样?因为调用时编译器不知道你要哪个返回值,它只看参数。

我的建议:重载函数的功能应该相似。比如都是“加法”,只是参数类型不同。别把“加法”和“打印”重载成同一个名字,那会让读代码的人一头雾水。

知识体系总览

函数基础 定义与声明 声明在前,定义在后 参数传递 值传递 / 引用传递 / const引用 返回值 void / 值返回 / 引用返回 函数重载 同名不同参,编译器匹配 常见陷阱 悬空引用 / 重载歧义 核心原则:职责单一,接口清晰,避免副作用 用好函数,代码质量提升一个档次

写在最后

函数是C++程序的基本组成单元。把大问题拆成小函数,每个函数只做一件事,做好一件事——这就是我写代码十几年来最深的体会。

刚开始你可能觉得写函数麻烦,但等你维护过几次几千行的代码后,你就会明白:好的函数设计,比注释更能说明代码的意图

记住三点:

  • 能用const引用就别用值传递(大对象尤其注意)
  • 别返回局部变量的引用
  • 重载的函数功能要相似,别乱来

公众号:蓝海资料掘金营,微信deep3321