一、引用传递的概念
上节课学习了值传递,我们知道值传递是单向传递,形参和实参占用不同的内存空间,形参的改变不会影响实参。
如果需要在子函数中对形参的更改影响到实参,就需要使用引用传递。
二、引用的基本概念
引用是一种特殊类型的变量,可以被认为是另一个变量的别名。就好比 Windows 系统中的快捷方式图标,通过引用名访问变量与通过原变量名访问变量的效果完全一样。
引用的声明
类型名 &引用名 = 已存在的变量;
示例:
int a = 2;
int &ra = a; // ra 是 a 的引用(别名)Code language: JavaScript (javascript)
注意:真正的标识符是 ra,不是 &ra。& 在这里是引用声明符,不是取地址运算符。
引用的规则
- 声明一个引用时,必须同时对它进行初始化,使它指向一个已存在的对象。
- 一旦一个引用被初始化,就不能改为指向其他对象。
- 一个引用从诞生开始就必须确定是哪个变量的别名,并且始终只能作为该变量的别名,不能另作他用。
int a = 2, b = 4;
int &ra = a; // ra 是 a 的别名
ra = b; // 这不是让 ra 指向 b,而是将 b 的值赋给 a
cout << a; // 输出 4Code language: JavaScript (javascript)
三、引用作为形参(引用传递)
如果将引用作为形参,函数调用时发生的参数传递就是引用传递。引用传递时,形参是实参的别名,形参和实参占用同一块内存空间。对形参的任何操作,实质上就是对实参的操作。
语法形式
返回类型 函数名(类型名 &形参名1, 类型名 &形参名2, ...)
{
语句序列
}
四、示例:用引用传递实现交换两个整数
#include <iostream>
using namespace std;
void swap(int &a, int &b)
{
int z = a;
a = b;
b = z;
}
int main()
{
int a = 2;
int b = 4;
cout << "交换前 a,b " << a << " " << b << endl; // 输出 2 4
swap(a, b);
cout << "交换后 a,b " << a << " " << b << endl; // 输出 4 2
return 0;
}Code language: PHP (php)
分析:
- main 中 a = 2, b = 4。
- 调用 swap(a, b) 时,形参 int &a 是实参 a 的别名,int &b 是实参 b 的别名。
- swap 函数内部交换形参 a 和 b 的值,实际上交换的就是 main 中实参 a 和 b 的值。
- swap 执行完毕,main 中的 a 和 b 已经被成功交换。
五、代码示例
以下代码演示了引用传递的各种应用场景:
#include <iostream>
using namespace std;
// 示例1:引用传递交换两个变量
void swap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
}
// 示例2:引用传递修改多个值
void divide(int dividend, int divisor, int "ient, int &remainder)
{
quotient = dividend / divisor;
remainder = dividend % divisor;
}
// 示例3:引用传递用于输入参数
void input(int &x)
{
cout << "请输入一个整数:";
cin >> x;
}
// 示例4:引用传递用于累加
void accumulate(int &sum, int value)
{
sum += value;
}
// 示例5:引用作为返回值(返回后不能引用局部变量)
int global = 100;
int& getGlobal()
{
return global;
}
// 示例6:常量引用(防止在函数内修改实参)
void printArray(const int &size)
{
// size = 10; // 错误:size 是常量引用,不能修改
cout << "size = " << size << endl;
}
// 示例7:结构体引用传递
struct Point
{
int x;
int y;
};
void movePoint(Point &p, int dx, int dy)
{
p.x += dx;
p.y += dy;
}
// 示例8:值传递 vs 引用传递对比
void byValue(int x)
{
x = 999;
}
void byReference(int &x)
{
x = 999;
}
int main()
{
// 测试交换
int a = 2, b = 4;
cout << "交换前 a=" << a << ", b=" << b << endl;
swap(a, b);
cout << "交换后 a=" << a << ", b=" << b << endl << endl;
// 测试多值返回
int q, r;
divide(17, 5, q, r);
cout << "17 / 5 = " << q << " 余 " << r << endl << endl;
// 测试输入
int num;
input(num);
cout << "你输入的是:" << num << endl << endl;
// 测试累加
int sum = 0;
accumulate(sum, 10);
accumulate(sum, 20);
accumulate(sum, 30);
cout << "累加结果:" << sum << endl << endl;
// 测试引用返回值
getGlobal() = 200;
cout << "global = " << global << endl << endl;
// 测试常量引用
int sz = 5;
printArray(sz);
cout << endl;
// 测试结构体引用传递
Point pt = { 1, 2 };
cout << "移动前 pt=(" << pt.x << "," << pt.y << ")" << endl;
movePoint(pt, 10, 20);
cout << "移动后 pt=(" << pt.x << "," << pt.y << ")" << endl << endl;
// 测试值传递 vs 引用传递
int test = 5;
byValue(test);
cout << "byValue 后 test=" << test << endl; // 仍为 5
byReference(test);
cout << "byReference 后 test=" << test << endl; // 变为 999
return 0;
}Code language: PHP (php)
六、值传递与引用传递的对比
| 特性 | 值传递 | 引用传递 |
|---|---|---|
| 形参类型 | 普通变量 | 引用变量(类型名 &) |
| 内存关系 | 形参和实参占用不同内存 | 形参是实参的别名,共用同一内存 |
| 传递方向 | 单向(实参 → 形参) | 双向(形参修改影响实参) |
| 初始化 | 用实参的值初始化 | 形参绑定到实参 |
| 适用场景 | 不需要修改实参 | 需要修改实参、大型对象避免复制 |
| 语法 | void func(int a) | void func(int &a) |
七、注意事项
- 引用必须在声明时初始化,且一旦绑定就不能更改指向。
- 引用传递中,形参是实参的别名,对形参的操作直接影响实参。
- 如果不需要在函数内修改实参,但希望避免大型对象的复制开销,可以使用常量引用(
const 类型名 &)。 - 不要返回局部变量的引用,因为局部变量在函数结束后被销毁,引用将指向无效内存。
- 引用作为形参时,函数调用时不需要写
&,直接传变量名即可。编译器自动进行引用绑定。 - 引用传递比指针传递更安全、更直观,是 C++ 中推荐的参数传递方式。
八、常见错误
1. 引用未初始化
int &r; // 错误:引用必须初始化Code language: HTML, XML (xml)
2. 返回局部变量的引用
int& func()
{
int x = 10;
return x; // 危险:x 被销毁后引用无效
}Code language: JavaScript (javascript)
3. 误以为引用可以重新绑定
int a = 1, b = 2;
int &r = a;
r = b; // 这是将 b 的值赋给 a,不是让 r 指向 bCode language: JavaScript (javascript)
4. 常量引用被修改
void func(const int &x)
{
x = 10; // 错误:常量引用不能修改
}Code language: JavaScript (javascript)
Previous: 函数的参数传递-值传递
Next: 内联函数