在看这篇文章看可以看下我之前写的关于C++中参数的引用传递的介绍,详情见 https://blog.csdn.net/wenhao_ir/article/details/51611388
目录
01-引用传递本质上是利用指针进行的操作
引用传递在本质上是通过指针来实现的,但引用和指针在语法和使用上有明显的区别。以下是详细分析:
1. 引用的本质
在 C++ 中,引用是变量的别名,实际上是对别的变量的一种绑定操作,这种绑定操作一旦建立,不可更改,引用的底层实现通常是通过指针来完成的。编译器会将引用转换为指针操作,但在语法层面,引用更直观和安全。
例如,以下代码:
int x = 10;
int &ref = x;
ref = 20; // 修改x的值
在底层可能会被编译器转换为:
int x = 10;
int *ptr = &x; // 引用被转换为指针
*ptr = 20; // 通过指针修改x的值
另外,由于对别的变量的绑定操作一旦建立,则不可更改,所以,如果在后面增加代码:
int y = 30;
ref = y;
那么ref
不会重新绑定到y,而是会将 y 的值赋给x,所以下面的代码执行完后:
int x = 10;
int &ref = x;
ref = 20; // 修改x的值
int y = 30;
ref = y;
ref = 40;
x的值为40,ref仍然是与x绑定,而不是改绑为y,y的值仍然为30。
上面这段代码带详细注释的版本如下:
int x = 10; // x 初始化为 10
int &ref = x; // ref 是 x 的引用,ref 和 x 绑定到同一个内存地址
ref = 20; // 修改 ref 也就是修改 x,现在 x 的值为 20
int y = 30; // y 初始化为 30
ref = y; // 将 y 的值赋给 ref 所引用的变量 x,x 的值变为 30
ref = 40; // 修改 ref 也就是修改 x,x 的值变为 40
2. 引用传递的定义
普通变量的引用定义
int &a = x; // 正确:引用 a 绑定到 x
int& b = y; // 也正确:b 绑定到 y
函数返回值为引用类型的定义
示例1:
int globalValue = 10; // 定义一个全局变量
// 函数返回 int 的引用
int& func() {
return globalValue; // 返回int类型全局变量的引用
}
注意:函数的返回值为引用类型时,符号&
与类型名不能分开写,即int& func() {}
你不能写成int & func() {}
示例2:
MyClass& setValue(int v) {
value = v;
return *this; // 返回当前对象的引用,注意this是MyClass类型对象的指针。
}
注意:函数的返回值为引用类型时,符号&
与类型名不能分开写,即int& func() {}
你不能写成int & func() {}
为了更好的理解示例2,我把示例2的完整代码给出来:
class MyClass {
private:
int value;
public:
MyClass& setValue(int v) {
value = v;
return *this; // 返回当前对象的引用
}
void show() {
std::cout << "Value: " << value << std::endl;
}
};
关于关键词this
的详细介绍见:https://blog.csdn.net/wenhao_ir/article/details/145512865
3. 函数中引用传递的本质
引用传递的本质也是通过指针实现的。例如:
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
在底层可能会被编译器转换为:
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
调用时:
swap(x, y);
会被转换为:
swap(&x, &y);
4. 引用与指针的区别
虽然引用在底层是通过指针实现的,但它们在语法和使用上有以下区别:
特性 | 引用 (& ) | 指针 (* ) |
---|---|---|
初始化 | 必须在声明时初始化,且不能更改引用对象 | 可以不初始化,也可以随时指向其他对象 |
空值 | 不能为空(必须绑定到一个有效对象) | 可以为空(nullptr 或 NULL ) |
语法 | 直接使用变量名,无需解引用 | 需要使用 * 解引用 |
安全性 | 更安全,避免了空指针和野指针问题 | 需要手动检查空指针和野指针 |
用途 | 通常用于函数参数传递和返回值优化 | 更灵活,适用于动态内存管理等场景 |
5. 引用传递的优点
引用传递在 C++ 中被广泛使用,主要因为它有以下优点:
- 语法简洁:不需要显式使用
*
和&
操作符。 - 安全性高:引用不能为空,避免了空指针问题。
- 可读性强:代码更直观,易于理解。
例如:
void print(int &value) {
cout << value << endl;
}
比以下指针版本更简洁:
void print(int *value) {
if (value != nullptr) {
cout << *value << endl;
}
}
6. 总结
- 引用传递在底层是通过指针实现的,但引用提供了更高级的抽象,使代码更简洁和安全。
- 引用和指针各有优缺点,选择使用哪种方式取决于具体场景:
- 如果需要更安全和简洁的语法,优先使用引用。
- 如果需要更灵活的操作(如动态内存管理),则使用指针。
02-C++类中的引用成员为什么初始化后就不能更改了?
因为 C++ 规定:
- 所有成员变量必须在对象构造完成前初始化。
- 成员函数的执行发生在对象构造完成之后。
- 上面我已经说了,C++ 的引用本质上是别名,一旦绑定到某个对象,就无法更改绑定。因此,引用类型的成员变量必须在对象构造时就初始化,否则无法保证其正确性。
一个错误示例如下:
class Example {
private:
int& ref; // 引用成员
public:
void init(int& r) {
ref = r; // ❌ 错误!引用已经初始化,不能再更改绑定
}
};
上面的错误示例更正后的代码如下:
class Example {
private:
int& ref;
public:
Example(int& r) : ref(r) {} // ✅ 正确,初始化列表中完成绑定
};
关于C++的初始化列表的介绍见:
https://blog.csdn.net/wenhao_ir/article/details/145422254