3. C++基础增强:引用、const增强、函数重载、默认参数、内联函数
好,咱们进入第三章。说实话,从C到C++,最直观的感受就是——写代码可以更“懒”了,也更安全了。这一章讲的是C++对C语言的一些基础增强。你别小看这些点,它们几乎是所有现代C++代码的基石。
我个人习惯把这一章叫做“C++的语法糖衣炮弹”。看着甜,用着爽,但用不好也会炸。咱们一个一个来拆。
3.1 引用:指针的“语法糖”,但不止是糖
引用是什么?说白了,就是给变量起个“别名”。你想想看,一个人有大名、小名、外号,但叫哪个名字都是同一个人。引用就是这个道理。
核心区别:引用必须初始化,且一旦绑定就不能再改绑。指针可以随时指向别处。
int a = 10;
int &ref = a; // ref 是 a 的引用
ref = 20; // 现在 a 也变成了 20
int *p = &a; // 指针可以指向 a
p = nullptr; // 指针可以改,引用不行
我在项目中遇到过一个问题:有个同事用指针传参,结果函数内部把指针指向了别处,外层完全没感知。用引用就永远不会出这种问题。引用在底层实现上就是指针,但语法上更安全。
我的建议:能用引用就别用指针。除非你需要“空值”语义,或者需要重新绑定,才用指针。
3.2 const 增强:C语言的“假常量”终于被治了
C语言的const,说实话有点“假”。你可以通过指针轻松绕过它。C++的const是真正的常量,而且用法丰富了很多。
嗯,这里要注意:C++的const变量默认是内部链接的。什么意思?就是你在头文件里定义 const int MAX = 100;,每个包含这个头文件的.cpp文件都有自己的副本。这在C语言里会报重复定义错误。
// C++ 中,const 是真正的只读
const int MAX_SIZE = 100;
// int *p = &MAX_SIZE; // 错误!不能把 const int* 赋给 int*
// const 成员函数:承诺不修改对象
class Student {
public:
int getAge() const { // 这个函数不会修改对象
return age;
}
private:
int age;
};
我曾经踩过的坑:在const成员函数里不小心调用了非const成员函数,编译报错。后来养成了习惯:只读操作一律加const,这样编译器会帮你检查。
3.3 函数重载:同名不同参,编译器帮你选
函数重载,说白了就是“一个名字,多种实现”。C语言里你要写 add_int、add_float、add_double,C++里直接写三个 add 就行。
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
// 调用时,编译器根据参数类型自动匹配
int main() {
int sum1 = add(1, 2); // 调用 int 版本
double sum2 = add(1.5, 2.3); // 调用 double 版本
}
你想想看,这背后是怎么实现的?C++编译器会对函数名进行“名字修饰”(name mangling),把参数类型信息编码进符号里。所以两个同名函数在底层其实是两个不同的符号。
注意:返回值类型不能作为重载的依据。为什么?因为调用时可能不接收返回值,编译器就不知道调用哪个了。
3.4 默认参数:少写代码,多活几年
默认参数,就是给函数参数一个“备胎值”。调用时你不传,就用默认值。这个特性看着简单,但用好了能省不少事。
void printLog(const char* msg, int level = 0) {
if (level == 0) {
printf("[INFO] %s\n", msg);
} else {
printf("[ERROR] %s\n", msg);
}
}
// 调用
printLog("系统启动"); // 默认 level=0
printLog("内存不足", 1); // 指定 level=1
我的习惯:默认参数放在函数声明里,不要在定义里重复写。否则别人看定义时还得去翻声明,很烦人。
还有一个坑:默认参数必须从右向左连续提供。你不能跳过中间的参数去给右边的参数设默认值。比如 void func(int a, int b = 0, int c); 这是错的。
3.5 内联函数:用空间换时间
内联函数,说白了就是建议编译器把函数调用替换成函数体代码。省去了函数调用的开销(压栈、跳转、返回)。
inline int max(int a, int b) {
return a > b ? a : b;
}
// 调用时,编译器可能直接展开成:
// int result = (x > y) ? x : y;
我记得早期做嵌入式开发时,有个高频调用的比较函数,改成 inline 后性能提升了将近 15%。当然,inline 只是“建议”,编译器有权忽略。比如递归函数、代码太长的函数,编译器通常不会内联。
我曾经犯过的错:把一个大函数加了 inline,结果编译出来的二进制体积暴增,反而因为缓存命中率下降导致性能变差。内联适合短小、频繁调用的函数。
另外,C++的 inline 还有一个隐藏功能:它可以让你在头文件里定义函数,而不会引起多重定义错误。这在写头文件库时特别有用。
3.6 这些特性怎么配合使用?
这几个特性不是孤立的。我举个例子,看看它们怎么协同工作:
class Vector2D {
public:
// const 成员函数 + 引用返回
const double& getX() const { return x; }
// 内联 + 默认参数
inline void setPosition(double nx, double ny = 0.0) {
x = nx;
y = ny;
}
// 重载:支持不同参数类型
void scale(double factor) {
x *= factor;
y *= factor;
}
void scale(int factor) {
x *= factor;
y *= factor;
}
private:
double x, y;
};
你看,const 保证 getX 不修改对象,引用返回避免拷贝,内联提升性能,默认参数让调用更灵活,重载支持不同类型。这些特性组合起来,代码既安全又高效。
总结一下:
- 引用:别名,安全,必须初始化
- const:真正的只读,成员函数加 const 是好习惯
- 重载:同名不同参,编译器自动匹配
- 默认参数:从右向左连续提供,声明里写
- 内联:短小函数用 inline,空间换时间
这些基础增强,说白了就是 C++ 在 C 的基础上做的“人性化改进”。每个特性都解决了一个实际痛点。你写代码时多想想:这个场景用 C 怎么写?用 C++ 怎么写?慢慢就能体会到设计者的用心了。