详解C++中引用传递的概念;C++类中的引用成员为什么初始化后就不能更改了?

在看这篇文章看可以看下我之前写的关于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. 引用与指针的区别

虽然引用在底层是通过指针实现的,但它们在语法和使用上有以下区别:

特性引用 (&)指针 (*)
初始化必须在声明时初始化,且不能更改引用对象可以不初始化,也可以随时指向其他对象
空值不能为空(必须绑定到一个有效对象)可以为空(nullptrNULL
语法直接使用变量名,无需解引用需要使用 * 解引用
安全性更安全,避免了空指针和野指针问题需要手动检查空指针和野指针
用途通常用于函数参数传递和返回值优化更灵活,适用于动态内存管理等场景

5. 引用传递的优点

引用传递在 C++ 中被广泛使用,主要因为它有以下优点:

  1. 语法简洁:不需要显式使用 *& 操作符。
  2. 安全性高:引用不能为空,避免了空指针问题。
  3. 可读性强:代码更直观,易于理解。

例如:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值