函数进阶:默认参数、函数重载、内联函数
说实话,很多C++初学者写函数,就是「定义-调用」两步走。但等你真正做项目了,就会发现函数的设计直接决定了代码好不好用、好不好维护。今天聊的三个特性——默认参数、函数重载、内联函数——说白了就是让你写出来的函数更灵活、更聪明、更快。
我个人习惯,在写工具类或者库函数时,默认参数和重载几乎是必用的。内联函数嘛,得看场景,用对了是性能利器,用错了反而帮倒忙。咱们一个一个说。
1. 默认参数:少写点代码,多留点余地
默认参数的意思很简单:函数声明时给参数一个默认值,调用时如果你不传这个参数,编译器就用默认值顶上。
void showLog(const std::string& msg, int level = 1) {
std::cout << "[Level " << level << "] " << msg << std::endl;
}
// 调用
showLog("系统启动"); // level 默认为 1
showLog("发生错误", 3); // 手动指定 level 为 3
我在项目中遇到过一种情况:一个日志函数有七八个参数,每次调用都要写一堆默认值,代码又长又丑。后来我把常用参数设了默认值,调用时只传关键信息,清爽多了。
注意:默认参数必须从右向左连续提供。你不能跳过中间的参数去给后面的参数设默认值。比如 void func(int a, int b = 1, int c = 2) 是合法的,但 void func(int a = 1, int b, int c = 2) 就不行。
还有一个坑:默认参数写在声明里还是定义里?我建议只写在声明里。如果你写在定义里,调用时如果看不到定义,编译器就不知道有默认值,会报错。
小技巧:如果你在写一个接口,不确定未来会不会增加参数,可以预留一个默认参数位。比如 void process(int id, int flag = 0),以后扩展时加个新参数,不影响旧代码。
2. 函数重载:同名不同人,编译器来认
函数重载,就是同一个函数名,参数列表不同(参数个数、类型、顺序不同),编译器根据调用时传入的参数自动匹配最合适的版本。
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;
}
// 调用
int sum1 = add(3, 5); // 调用 int 版本
double sum2 = add(2.5, 3.7); // 调用 double 版本
std::string sum3 = add("Hello", " World"); // 调用 string 版本
你想想看,如果没有重载,你得写三个不同的函数名:addInt、addDouble、addString。调用时还得记名字,多麻烦。重载让代码更自然,更符合直觉。
核心原则:重载函数的返回值类型可以不同,但仅靠返回值类型不同是无法构成重载的。编译器只看参数列表,不看返回值。所以 int foo() 和 double foo() 不能同时存在,编译器会报错。
我曾经踩过一个坑:写了一个重载函数,参数一个是 int,一个是 long。调用时传了一个 short 类型的变量,结果编译器报「二义性调用」。因为 short 既可以转成 int 也可以转成 long,编译器不知道选哪个。嗯,这里要注意:重载时尽量避免参数类型过于接近,尤其是整数类型。
3. 内联函数:用空间换时间
内联函数,关键字 inline,它的作用是建议编译器把函数调用替换成函数体本身,省去函数调用的开销(压栈、跳转、返回等)。
inline int square(int x) {
return x * x;
}
// 调用 square(5) 时,编译器可能直接替换为 5 * 5
说白了,内联函数就是让代码跑得更快。但注意,inline 只是一个建议,不是命令。编译器有权忽略它。比如函数体太大、有递归、有循环,编译器通常不会内联。
警告:不要滥用内联。如果一个函数体很长,内联会导致代码膨胀,反而降低缓存命中率,拖慢性能。我一般只对 3-5 行以内的简单函数使用内联,比如 getter、setter、简单的数学运算。
我记得有一次优化一个高频调用的数学库,把几个核心函数改成内联,性能提升了将近 20%。但后来有个同事把一个大函数也加了 inline,结果编译出来的二进制体积翻了一倍,性能反而下降了。所以,内联不是万能药,得对症下药。
建议:现代编译器优化能力很强,很多时候你不需要手动加 inline。如果你真的关心性能,先用 profiler 找到热点,再考虑是否内联。不要凭感觉。
知识体系总览
下面这张图帮你理清这三个特性的关系和应用场景:
三者如何配合使用?
实际项目中,这三个特性经常一起出现。比如你写一个绘图库:
class Renderer {
public:
// 默认参数:不传颜色就用黑色
void drawLine(int x1, int y1, int x2, int y2,
const std::string& color = "black");
// 重载:支持不同形状
void drawCircle(int cx, int cy, int radius,
const std::string& color = "black");
void drawRect(int x, int y, int w, int h,
const std::string& color = "black");
// 内联:简单的颜色转换
inline std::string toHex(const std::string& colorName) {
if (colorName == "red") return "#FF0000";
if (colorName == "green") return "#00FF00";
return "#000000";
}
};
你看,默认参数让调用者少写参数,重载让不同形状共用同一个命名风格,内联让颜色转换这种高频操作更快。三者各司其职,配合起来很舒服。
总结一下:默认参数解决的是「可选参数」的问题,函数重载解决的是「同名不同逻辑」的问题,内联函数解决的是「性能敏感」的问题。掌握好这三个工具,你的函数设计会上一个台阶。
好了,今天就聊到这儿。代码写多了你会发现,好的函数设计不是炫技,而是让调用者用得舒服,让维护者看得明白。希望今天的分享对你有帮助。