函数定义与重载
更新时间:2026-08-27。本文是
languages/cpp/主题入门层第 11 篇。函数是代码的零件箱,C++ 的函数跟 C 的几乎一样,但多了个 C 想都不敢想的本事——重载:同名函数可以有好几个版本,靠参数类型区分。这也是进入类与对象之前最后一块拼图。
本文要回答的问题
- C++ 的函数定义和 C 有什么不同?
- 重载是什么?凭什么能重载?
- 重载的边界在哪?哪些情况会冲突?
一、函数定义:和 C 几乎一样
int add(int a, int b) {
return a + b;
}
void print_log(const std::string& msg) { // 参数用 const 引用,先记住这个写法
std::cout << msg << "\n";
}结构、return、先声明后使用的规矩和 C 一致。差异主要在细节:
- 参数可以带默认值(下一篇细讲);
- 可以传引用(13/14 篇细讲),这是 C 没有的;
- 重载:同名函数多个版本,本篇主角;
void表示不返回值,main照旧由系统调用。
C 里"先声明后使用"那条规矩依然适用:定义在调用点之后就得先写原型声明。多文件工程声明放头文件,规矩和 C 完全一样。
二、重载:同名不同参
C 里你不能写两个都叫 add 的函数,编译器直接报"重复定义"。C++ 允许:
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
std::string add(const std::string& a, const std::string& b) {
return a + b;
}
add(1, 2); // 调 int 版
add(1.5, 2.5); // 调 double 版
add(std::string("a"), std::string("b")); // 调 string 版编译器根据调用时实参的类型挑最合适的那一版。规则:参数类型、个数、顺序不同,就能构成重载;只靠返回类型区分不行:
int f(int) { return 0; }
double f(int) { return 0.0; } // 错误:仅返回类型不同,不构成重载因为调用 f(5) 时编译器根本不知道你要哪个返回值——它只看参数。
重载在标准库里到处都是:std::to_string(42)、std::to_string(3.14) 就是同一函数名配不同类型参数。
三、重载的底层:name mangling
C 编译两个同名函数会报"符号冲突",因为 C 的符号名就是函数名本身;C++ 编译器会把函数名 + 参数类型编码进符号里,比如 add(int, int) 可能变成 _Z3addii。这叫 name mangling(名字改编)。

这也解释了 C++ 的函数符号不兼容 C——extern "C" 就是为了告诉编译器"这个函数按 C 的规则命名"。这块的完整机制(符号表、重定位)在专家层符号表与重定位和 C++ ABI 与 name mangling展开,这里先知道"重载不是魔法,是符号不同"。
四、重载的坑
| 坑 | 现象 | 对策 |
|---|---|---|
| 仅返回类型不同 | 编译错误 | 重载必须参数不同 |
| 参数类型可隐式转换 | f(5) 调了 f(double) 而不是预期的 f(int) | 注意重载决议,必要时显式转换 |
int vs long 调不明确 | 两个重载都能接收 | 避免接近的整数类型同时重载 |
| 默认参数和重载叠加 | 调用有歧义 | 见下一篇,谨慎组合 |
第二条值得多说一句:重载决议时会考虑隐式转换。f(5) 里 5 是 int,如果只有 f(double) 一个版本,编译器会默默转成 double 调用它——行为没错,但如果你同时还有 f(int),它会优先选 f(int)。规则的细节很多,入门记住一条:别让两个重载的入参都能被同一个实参隐式转换。
五、与本站主线衔接
- 函数调用的栈帧模型、值传递细节,C 篇讲透了,见 C 函数基础;
- 调用约定、寄存器级细节见专家层内存布局与 ABI;
- name mangling 与 ABI 细节,见 C++ ABI;
- 下一篇讲默认参数——重载的近亲,两者经常一起出现。
一句话总结
C++ 函数定义沿用 C 的骨架,重载允许同名函数靠参数类型/个数/顺序区分,底层是编译器把参数信息编进符号(name mangling);只改返回类型不算重载,重载决议会考虑隐式转换,别让两个版本都能被同一实参匹配;extern "C" 是 C/C++ 混编的开关。
上一篇:auto 与类型推导 下一篇:默认参数