10. 引用:引用概念、引用与指针区别、常引用、引用作为函数参数与返回值

引用,是 C++ 里一个非常有意思的特性。说实话,我刚从 C 转到 C++ 时,觉得引用就是个「语法糖」,没啥大不了的。后来写项目多了才发现——嗯,这玩意儿真香。

今天我们就来彻底搞懂引用。我会把引用概念、和指针的区别、常引用、以及怎么用引用做参数和返回值,一次讲清楚。

10.1 引用的概念

引用,说白了就是给一个变量起个「别名」。你叫张三,你朋友叫你「三哥」,你妈叫你「儿子」——都是指同一个人。引用就是这个道理。

语法很简单:

int a = 10;
int &b = a;  // b 是 a 的引用

这里 b 就是 a 的别名。操作 b 就是操作 a,它们俩在内存里是同一个地址。

核心要点:引用必须在定义时初始化,而且一旦绑定,就不能再绑定到其他变量。

我刚开始学的时候犯过一个低级错误:以为引用可以像指针一样重新赋值。结果代码跑起来全是 bug。后来我记住了——引用从一而终,不能「劈腿」。

10.2 引用 vs 指针

很多初学者会问:引用和指针到底有啥区别?我直接给你列个表,一目了然。

对比项 引用 指针
初始化 必须初始化 可以不初始化
重新绑定 不可以 可以
空值 不能为空 可以为 nullptr
内存占用 不额外占用(编译器优化) 占用 4/8 字节
使用方式 像普通变量一样用 需要解引用 *
安全性 更安全 容易出野指针

我个人习惯是:能用引用就用引用。指针只有在需要「重新指向」或者「可能为空」时才用。你想想看,引用不用判空,不用解引用,多省心。

避坑指南:我曾经在一个大型项目里,因为用了指针判空漏了一个分支,导致线上崩溃。后来重构时全改成引用,这类问题再没出现过。

10.3 常引用

常引用,就是 const T&。它表示:通过这个引用,你不能修改原变量的值。

int a = 10;
const int &b = a;
// b = 20;  // 编译错误!不能通过常引用修改

常引用最大的用处是——保护数据。你想想看,如果你写一个函数,只想读取数据,不想修改它,那就用常引用。

还有一个隐藏特性:常引用可以绑定临时对象。

const int &r = 42;  // 合法!临时对象被延长生命周期
// int &r2 = 42;    // 编译错误!普通引用不能绑定临时对象

这个特性在函数传参时特别有用。比如你传一个字符串字面量或者表达式结果,常引用能接住,普通引用就不行。

小技巧:函数参数如果只读,优先用 const T&。这既避免了拷贝开销,又保证了数据安全。我在写代码时,90% 的引用参数都是常引用。

10.4 引用作为函数参数

引用做参数,最大的好处就是——避免拷贝。尤其是传大型对象时,拷贝的开销非常大。

// 值传递:拷贝整个对象
void func1(MyClass obj) { ... }

// 引用传递:只传别名,不拷贝
void func2(MyClass &obj) { ... }

// 常引用传递:不拷贝,且不能修改
void func3(const MyClass &obj) { ... }

我做过一个测试:传一个 1MB 的结构体,值传递耗时约 1.2ms,引用传递不到 0.001ms。差距上千倍。所以,大对象一定要用引用传参。

引用传参还有一个用途——输出参数。函数可以通过引用修改外部变量。

void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

int x = 1, y = 2;
swap(x, y);  // x=2, y=1

这个例子很经典。用指针也能实现,但引用写起来更自然,调用时不用取地址。

注意:如果函数不需要修改参数,一定要加 const。不加 const 的话,调用方会担心数据被改,而且不能传常量进去。

10.4 引用作为函数返回值

引用做返回值,可以避免返回时拷贝。但这里有个大坑——不能返回局部变量的引用。

// 错误!返回局部变量的引用
int& badFunc() {
    int a = 10;
    return a;  // a 在函数结束时销毁,引用变成悬空引用
}

那什么时候用引用返回值呢?常见场景是:返回类成员变量、返回全局变量、或者返回静态变量。

class Array {
    int data[100];
public:
    int& operator[](int index) {
        return data[index];  // 返回引用,可以修改
    }
};

Array arr;
arr[0] = 42;  // 通过引用修改成员

这个例子中,operator[] 返回引用,所以 arr[0] = 42 才能正常工作。如果返回值传递,就只能读不能写了。

还有一个经典用法——链式调用。

class Counter {
    int count = 0;
public:
    Counter& add(int n) {
        count += n;
        return *this;  // 返回对象本身的引用
    }
};

Counter c;
c.add(5).add(3).add(2);  // 链式调用

这种写法在流操作、建造者模式中非常常见。返回 *this 的引用,就能实现连续调用。

避坑指南:我曾经在返回引用时,不小心返回了一个临时对象的引用。程序跑起来偶尔崩溃,查了两天才找到原因。记住:返回引用时,确保对象的生命周期比引用长。

10.5 知识体系总览

下面这张图,把引用的核心知识点串起来了。你可以把它当作复习提纲。

C++ 引用 引用概念 • 变量的别名 • 必须初始化 • 绑定后不可变 引用 vs 指针 • 引用更安全,无需判空 • 指针可重新指向 • 引用使用更简洁 常引用 • const T& • 保护数据不被修改 • 可绑定临时对象 引用作为参数 • 避免拷贝开销 • 可做输出参数 • 优先用 const T& 引用作为返回值 • 避免返回时拷贝 • 不能返回局部变量 • 支持链式调用 核心原则:能用引用不用指针,能加 const 就加 const

引用这个特性,说难不难,说简单也不简单。关键是要理解它的本质——别名。一旦你理解了「引用就是另一个名字」,很多用法就顺理成章了。

我个人觉得,引用是 C++ 里设计得最优雅的特性之一。它让代码更安全、更简洁。你写多了就会爱上它。


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