3. C++基础增强:引用、const增强、函数重载、默认参数、内联函数

好,咱们进入第三章。说实话,从C到C++,最直观的感受就是——写代码可以更“懒”了,也更安全了。这一章讲的是C++对C语言的一些基础增强。你别小看这些点,它们几乎是所有现代C++代码的基石。

我个人习惯把这一章叫做“C++的语法糖衣炮弹”。看着甜,用着爽,但用不好也会炸。咱们一个一个来拆。

C++ 基础增强核心知识体系 C++ 基础增强 五大核心 引用 (Reference) const 增强 函数重载 默认参数 内联函数 每个特性都解决了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++ 怎么写?慢慢就能体会到设计者的用心了。

可可精品资料库