1. 引用的基本定义与语法格式
定义:引用(Reference)是已存在变量的别名,引用和被引用的变量共享同一块内存,操作引用等同于操作原变量。
int a = 10; int &ref = a; // ref 是 a 的别名 ref = 20; // 等价于把 a 改成 20考点说明:
- 【考研选择题高频】引用在定义时必须初始化,否则编译错误。
- 【面试高频】引用一旦初始化后不能改变绑定对象,不存在“把引用重新指向另一个变量”的操作。
- 语法格式为:
类型 &引用名 = 已存在同类型变量;,取地址运算符&在此处表示引用声明。
2. 引用与指针的核心区别
| 对比维度 | 引用 | 指针 |
|---|---|---|
| 本质 | 已有变量的别名 | 独立变量,保存地址 |
| 是否必须初始化 | 必须初始化 | 可以先声明后赋值 |
| 能否为空 | 没有空引用 | 有 nullptr |
| 能否重新绑定 | 不能 | 可以 |
| 访问方式 | 直接使用,无需解引用 | 需要 * 解引用 |
| 多级 | 没有二级引用 | 支持二级、多级指针 |
| 自增自减 | 作用于原变量 | 作用于指针地址 |
代码对比:
int a = 10; int &r = a; // 引用,必须初始化 int *p = &a; // 指针,保存 a 的地址 r = 20; // 修改 a 的值为 20 *p = 30; // 通过解引用修改 a 的值 int b = 40; r = b; // 不是重新绑定,而是赋值:a 变成 40 p = &b; // 指针重新指向 b考点说明:
- 【考研选择题高频】“引用必须初始化、不能重新绑定”与“指针可以重新指向”的对比常以选择或判断题出现。
- 【面试高频】“引用底层是否占用内存”通常回答:标准不强制规定,多数编译器底层按指针方式实现,但语义上把它当作别名理解。
- 容易混淆的点:
r = b是赋值,p = &b是改变指向,二者含义完全不同。
3. 常量引用的特性与使用场景
特性:常量引用即指向 const 的引用,特点是不能通过该引用修改原对象,但可以绑定普通变量、const 变量以及临时对象或字面量,并且可以延长临时对象的生命周期。
int a = 10; const int &r1 = a; // 绑定普通变量 const int &r2 = 20; // 绑定字面量/临时量,合法 a = 30; // 合法:通过原变量修改 // r1 = 40; // 非法:不能通过常量引用修改 // int &r3 = 20; // 非法:普通引用不能绑定字面量使用场景:
- 函数参数使用
const T&,既能避免大对象拷贝,又能防止函数内部误改实参。 - 接收函数返回的临时对象,避免不必要的拷贝并保证安全。
考点说明:
- 【考研选择题高频】普通引用不能绑定字面量或临时对象,常量引用可以,这是高频对比考点。
- 【面试高频】
const T&绑定临时对象会延长临时对象的生命周期;绑定类对象时还可能涉及临时对象创建。
4. 引用作为函数参数与返回值的规则
4.1 作为函数参数
传引用可以避免拷贝,并允许函数直接修改实参。
void swap(int &x, int &y) { int tmp = x; x = y; y = tmp; }考点说明:
- 若只想避免拷贝且禁止修改,应使用
const T&;若需要修改实参,使用T&。 - 【面试高频】值传递、指针传递与引用传递的区别:值传递产生拷贝,指针传递需要解引用且可能为空,引用传递语法更简洁且不能为空。
4.2 作为函数返回值
int arr[10] = {0}; int &getElem(int i) { return arr[i]; } getElem(3) = 8; // 函数调用可以作为左值使用规则与易错点:
- 【考研/面试高频】绝不能返回局部变量的引用,否则函数返回后局部变量被销毁,产生悬空引用。
- 返回引用时,函数调用表达式可作为左值使用,这是引用返回值的重要用法。
- 若不允许调用者修改,应返回
const T&。
错误示例:
int &func() { int x = 10; return x; // 错误:返回局部变量的引用 }5. 左值引用与右值引用的基础概念
左值:有名字、可取地址、能够持续存在的对象;右值:字面量、临时对象、运算产生的中间结果等,通常无法取地址。
左值引用T&只能绑定左值;右值引用T&&可以绑定右值,主要用于移动语义和完美转发。
int a = 10; int &lv = a; // 左值引用绑定左值 a // int &err = 10; // 错误:左值引用不能绑定字面量 int &&rv = 20; // 右值引用绑定字面量 rv = 30; // 右值引用本身可以作为左值修改 int &&r1 = 10; int &r2 = r1; // 合法:具名的右值引用变量本身是左值考点说明:
- 【考研选择题高频】具名的右值引用变量本身是左值,可以被左值引用绑定,也能取地址。
- 【面试高频】右值引用的两大用途是移动构造、移动赋值以及实现完美转发,基础面试常问左右值判断。
6. 面试与考研高频易错点汇总
- 引用必须初始化:未初始化的引用声明是编译错误。
- 引用不能重新绑定:给引用赋值是修改原对象,不是更换对象。
- 普通引用与临时对象:普通引用不能绑定字面量或临时对象,
const T&可以并延长生命周期。 - 返回引用的大忌:返回局部变量的引用会产生悬空引用。
- 右值引用变量本身是左值:具名右值引用变量可以被左值引用绑定。
- 数组引用写法:
int (&r)[10]是数组引用,int &r[10]试图定义引用数组,后者不合法。 - 指针与引用的区别:指针可为空、可重新指向;引用必须绑定有效对象且不可更改。
- 函数参数选择:大对象只读用
const T&,需要修改用T&,内置类型通常直接值传递。
7. 典型例题解析
例题 1(考研选择题风格)
题目:下列关于引用描述正确的是?
- A.
int &r;定义了一个空引用 - B.
int a = 1; int &r = a; r = 3;会把 a 修改为 3 - C.
int &r = 10;可以正确绑定字面量 - D. 引用在使用前必须解引用
答案与解析:B 正确。r = 3等价于修改 a。A 错在引用必须初始化;C 错在普通引用不能绑定字面量;D 错在引用直接使用,无需解引用。
例题 2(面试问答)
题目:下面的函数有什么问题?
int &func() { int x = 10; return x; }解析:函数返回了局部变量 x 的引用。x 在函数结束时被销毁,调用者拿到的是悬空引用,继续使用会引发未定义行为。
例题 3(易混淆判断)
题目:判断正误:int &&r = 10; int &lr = r;编译错误,因为 r 是右值引用。
解析:判断为错误。虽然 r 是右值引用变量,但 r 具名、可取地址,本身是左值,可以被左值引用绑定,代码合法。
8. 自测题
题目 1(单选)
关于引用说法错误的是:
- A. 引用是变量的别名
- B. 引用可以重新绑定到另一个变量
- C. 引用定义时必须初始化
- D. 没有空引用
答案:B。引用一旦绑定不可更改。
题目 2(代码输出)
以下代码输出结果是:
int a = 1; int &r = a; int b = 2; r = b; cout << a << endl;- A. 1
- B. 2
- C. 编译错误
- D. 未定义行为
答案:B。r = b是赋值,a 被改为 2。
题目 3(单选)
以下哪条语句可以编译通过?
- A.
int &r = 10; - B.
const int &r = 10; - C.
int &r; - D. 把左值绑定到右值引用变量
答案:B。const 引用可以绑定字面量。
题目 4(简答)
编写一个函数,交换两个整数。分别说明值传递、指针传递、引用传递是否能完成交换,并说明原因。
参考答案:值传递修改的是形参副本,不能交换原变量;指针传递通过解引用修改指向对象,可以交换;引用传递直接操作实参别名,可以交换,且语法比指针简洁。示例:void swap(int &x, int &y)。
题目 5(判断并改错)
函数返回引用时,可以返回函数内部的静态局部变量的引用。
答案:说法正确。静态局部变量生命周期贯穿整个程序,函数结束后仍然存在,返回其引用是安全的;但普通局部变量不能返回引用。