理解 C++ 中的引用 (Reference)

什么是引用

引用是一个别名,也就是某个已存在的变量的另一个名字。对某个对象的引用进行操作,就是直接对这个对象进行操作。

创建一个引用

创建一个引用的语句如下:

类型标识符 & 引用变量名 = 目标变量名;

例如:

// 原始变量
int    a;
double b;

// 声明引用变量
int&    ref_a = a;
double& ref_b = b;

我们尝试对原始变量赋值,并且打印原始变量和引用变量的值:

#include <iostream>

using namespace std;

int main(){
    // 原始变量
    int    a;
    double b;

    // 声明引用变量
    int&    ref_a = a;
    double& ref_b = b;

    a = 9;
    cout << "a:" << i << endl;
    cout << "ref_a:" << r << endl;

    b = 6.7;
    cout << "b:" << d << endl;
    cout << "ref_b:" << s << endl;

    return 0;
}

编译运行,输出结果是:

a: 9
ref_a: 9
b: 6.7
ref_b: 6.7

这个输出结果是符合预期的。

引用的特点

与指针相区别,引用有以下的特点:

首先,不存在空引用。引用 只能也必须 在创建时被初始化为一个已存在的变量(连接到一块合法内存)。而创建指针时可以不赋初值,可以在任何时候被初始化,也可以为空。

int a = 9;
int& ref_a = a; // 正确,引用变量 ref_a 被初始化为 a
int& ref_a; // 错误,未给 ref_a 初值,编译会出错

第二,引用不能更换目标。一旦引用被初始化为一个对象,就不能被指向另一个对象。而指针可以在任何时候重新指向另一个对象。

double a = 17.09;
double b = 6.7;
double& ref_a = a; // 至此正确
double& ref_a = b; // 出错,不能多次指向

引用的应用

引用作为参数:以实现一个交换函数为例

下面我们以实现一个交换函数为例,对比指针和引用的使用方法。

void swap(__,__){

}

int main (){
    double a = 17.09, b = 6.7;
    swap(__,__);
    return 0;
}

引用实现

void swap(double &x, double &y){
    double temp;
    temp = y;
    y = x;
    x = temp;
}

调用时使用:

swap(a,b);

指针实现

void swap(double *x, double *b){
    double temp;
    temp = *x;
    *x = *y;
    *y = temp;
}

调用时使用:

swap(&a,&b);

常引用

声明方式:const 类型标识符 & 引用变量名 = 目标变量名;

以 const 方式声明的引用,则不能通过引用对目标变量的值进行修改,从而使目标变量成为 const,比较安全。

int a;
const int &ref_a = a;
ref_a = 1; // 错误
a = 1; // 正确

引用作为返回值

声明方式:

类型标识符 & 函数名 (形参列表及类型说明){
    函数体
}

当函数返回一个引用时,实际上返回一个指向返回值的隐式指针。这使得函数就可以放在赋值语句的左边。例如:

#include <iostream>

using namespace std;

int vals[] = {6, 0, 7};

int& setValues(int i){
    return vals[i]; // 返回第 i 个元素的引用
}

int main(){
    setValues(1) = 1; // 改变第 2 个元素
    return 0;
}

更多细节

  • 存在指针数组,但不存在引用数组,也不能建立数组的引用。

    int* a[3] = {&x,&y,&z}; // 正确,定义了一个包含三个整型指针变量的指针数组
    int& a[3] = {x,y,z}; // 错误,不存在引用数组
    
    int b[2] = {6,7};
    int& ref_b = b; // 错误,不能对数组的建立引用
    
  • 引用本身不是一种数据类型,编译器不给引用分配存储单元。

  • 区分 & 用作引用变量和用作取地址符。

    int a = 9;
    int& ref_a = a; // & 用于引用变量的创建
    
    int b = 100;
    int *p_b;
    p_b = &N; // & 用作取地址符
    
  • &&r &*p 不被接受,而 *&p 可被接受。
    例如,以下的程序是不被接受的。

    int a;
    int &&ref = a; // 错误
    int &*p = a; // 错误
    

    但是如下程序是正确的:

    int *p;
    int *& q = p;
    

    (int *)(&q) = p;,相当于给指针 p 起了别名 q。

  • 引用在底层仍然是指针实现的,但引用更加符合面向对象和隐藏实现细节的原则,通过使用引用来替代指针,可以使 C++ 程序更容易阅读和维护,一定程度上避免了指针的几个缺点。例如指针可以为空,对指代对象不能为空的指针要做 null 检查,指针在赋值方面更加灵活,当然也更容易出错。

猜你喜欢

转载自blog.csdn.net/weixin_43953703/article/details/104603877