• 猿创征文 |【C++】C++中的引用


    一、C++中的引用与C的联系

    一句话概括:
    C++中的引用,是C中指针的升级版。

    二、引用的概念

    引用是C++对C的一个重要扩充。
    作用是给变量起个别名。
    对引用的操作与对变量直接操作完全一样。 (类似于linux中的硬链接文件)

    引用不是定义一个新变量,而是给已存在的变量取了一个外号,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间

    举个形象的例子,鲁智深又被叫做"花和尚",这里的花和尚和鲁智深都是同一个人,花和尚就是鲁智深的引用,说白了引用其实就是取外号。

    三、定义引用

    & 在定义引用时,作用是引用标识符,标识定义的是一个引用;

    在C++中 & 有三个作用:

    1. 定义引用时,表示引用标识符,标识定义的是一个引用;
    2. 有两个操作数时,a&b,位运算的 按位与;
    3. 其他场景都表示取变量地址的意思;

    定义引用格式:
    类型名 &引用名 = 引用的目标;
    如:

    int a = 10;
    //定义一个引用b 引用的目标是a
    //定义成功后,使用b 和使用 a就是一样的了
    int& b = a;
    
    • 1
    • 2
    • 3
    • 4

    要求:

    1. 定义引用时必须要有引用的目标来初始化;
    2. 引用和引用的目标类型要保持一致;(继承和多态除外)
    3. 引用的目标一旦确定了,后面就不能再修改引用的目标了;

    四、引用存在的意义

    C++的面向程序较大型化,所以使用引用,可以避免指针指向错误的空间,而出现程序的崩溃或严重BUG。

    以下面代码为例:

    #include 
    using namespace std;
    
    int add(int *a,int *b)
    {
        //a++;如果偏移会出现越界访问错误
        return *a + *b;
    }
    int main()
    {
        int a=10;
        int b=20;
        int *p;//野指针,p中指向的地址是随机的
        //*p=100;出错
        int *const p1=&a;
        //p1++;报错
        int c=20;
        //p1=&c;报错
        cout << add(&a,&b) << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    总结:

    1. 在C++中const修饰的变量,一定要进行初始化;
    2. 在程序中不小心对指针进行偏移操会出现访问越界错误;单纯使用指针的方式无法保证代码的稳定性。
    3. 可能出现野指针问题;
    4. 应当用const修饰指针变量避免以上出现的问题;

    所以引用就来了。

    五、引用的相关用法

    5.1 基本用法

    #include 
    using namespace std;
    
    int main()
    {
        int a=10;
        int b=60;
        int& c=a;
        cout << a << "," << c <<endl;
        cout << &a << "," << &c <<endl;
        cout << "------------------" <<endl;
        a=20;
        cout << a << "," << c <<endl;
        cout << "------------------" <<endl;
        c=30;
        cout << a << "," << c <<endl;
        cout << "------------------" <<endl;
        c=b;
        cout << a << "," << c <<endl;
        cout << &a << "," << &c <<endl;
        cout << &b << "," << &c <<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    结果展示:
    在这里插入图片描述
    从结果我们可以发现:

    这个引用b变量,并没有开辟新的空间。

    c = b;
    
    • 1

    这种用法不是改变 c 引用的目标,而是将 b 的值赋给 r 一份儿。

    5.2 引用做形参

    好处:不用再考虑值传递和地址传递的问题了

    #include 
    using namespace std;
    int myfun(int& aa)
    {
        cout << "myfun():" << &aa << endl;
        aa=aa+10;
    }
    int main()
    {
        int a=10;
        cout << "main():" << &a <<endl;
        myfun(a);
        cout << a << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    结果展示:
    在这里插入图片描述
    分析结果可知:
    a 的地址和 aa 的地址是一样的,所以使用 aa 就是使用 a;
    总结:
    使用引用并没有开辟空间,在函数传参是最为常用的一种方式。

    自此学了引用之后,函数传参,如果是结构体的话,推荐大家使用引用的方式进行传参。

    同时,为了代码稳定与健状性,推存使用const修饰的引用做为函数的形参。

    5.3 常引用

    const修饰的引用被称为常引用。
    const修饰的引用,不仅可以引用左值,也可以引用右值。

    什么是左值?什么是右值?

    左值:有地址的量即左值,比如变量就是一个左值,因为变量是有地址的。一个左值,既可以放在 = 号左边,也可以放在 = 号的右边。

    右值:没有地址的量即一个右值,立即数就是一个右值,常量字符串也是一个右值。只能放在 = 号的右边。

    C++中有这种常引用,主要是用来保护数据的。同时,这种引用即可以引用左值,也可以引用右值。
    (C++中的常引用,即为一种语法糖)
    常引用举例:

    #include 
    using namespace std;
    int myfun(const int& a,const int& b)
    {
        cout << a+b << endl;
        //a=600;错误,不能通过常引用修改引用目标的值。
    }
    
    void show(const string& name)
    {
        //name="xiaoming";使用常引用就可以避免这种问题的出现。
        cout << name << endl;
    }
    int main()
    {
        int a = 10;
        int b = 20;
        myfun(a,b);
    
        int c = 100;
        const int& c1 = c;
        //c1=500;报错,
        c=500;//不影响c,因为c的类型在定义时就已经确定了 是没有const的
    
        //const int& c2 = 100;//常引用可以引用常量,C++的语法糖。
        //语法糖的底层实现。
        int temp = 100;
        const int& c2 = temp;
        //const修饰的这种常引用或者说叫左值引用,比单纯使用引用对C++程序代码来讲更具有意义。
        //因为他就是一种安全性的标识。
        //int &c3 = 100;//普通的引用不能
    
        const int& c4 = c+100;//常引用可以引用临时值
        //int &r5 = c+100;//普通的引用不能
    
        const int m = 10;
        //int& r6 = m; //引用const变量时也需要常引用
        const int& r6 = m;
    
        string name="yemaoxu";
        show(name);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    总结:

    1. 不能通过常引用修改引用目标的值;
    2. 常引用可以引用常量,普通的引用不能;
    3. 常引用可以引用临时值,普通的引用不能;
    4. 引用const变量时也需要常引用;
    5. 语法糖的底层实现
    int temp = 100;
    const int& c2 = temp;
    
    • 1
    • 2

    5.4 引用指针

    #include 
    #include 
    using namespace std;
    
    typedef struct Node{
        int data;
        struct Node *next;
    }node_t;
    
    //引用指针的用法
    //node_t * &r = phead;
    void create_node(node_t * &r, int data){
        r = (node_t *)malloc(sizeof(node_t));
        r->data = data;
        r->next = NULL;
    }
    
    int main(){
        node_t *phead = NULL;
        create_node(phead, -1);
        
        phead->data = 100;
        cout<<phead->data<<endl;
        free(phead);
        phead = NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    结果展示:
    在这里插入图片描述

    5.5 引用做返回值

    我们平时使用的函数,返回值都是一个右值;
    但是引用作为返回值,返回的是一个左值;

    引用做返回值时,不能返回局部变量的引用,因为局部变量占用的空间
    在函数结束时,就被操作系统回收了;
    可以返回全局变量的引用,或者static修饰的局部变量的引用。

    #include 
    #include 
    using namespace std;
    int& add(const int& a,const int& b)
    {
        //int temp=a+b;
        static int temp=a+b;
        return temp;
    }
    
    int main()
    {
        int a=10;
        int b=20;
        int ret=add(a,b);
        cout << ret <<endl;
    
        add(a,b)=100;
        cout << add(a,b) <<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    结果展示:
    在这里插入图片描述
    总结:

    1. 引用做返回值,返回的是一个左值;
    2. 不能返回局部变量的引用;

    5.6 结构体中存在引用成员

    #include 
    #include 
    using namespace std;
    struct Work
    {
        int a;
        int& b;
    };
    
    int main()
    {
        int m=20;
        //struct Work work1;错误的
        struct Work work2={10,m};//必须初始化引用成员才可以
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    总结:
    结构体中存在引用成员,必须初始化引用成员。

    六、引用和指针的特点及区别总结

    从编译器角度来讲:

    引用就是一种升级版的指针。

    从语法形式来讲:

    1. 引用是引用的是已经存在一块合法的空间。
    2. 引用变量即是引用空间的变量的别名。
    3. 指针可以是一个野指针,他可以指向任何的地方。
    4. 指针可以进行无限次的赋值,引用只可以被引用一次。
    5. 引用必须初始化,指针可以不初始化;
    6. 引用不可以改变指向,指针可以;
    7. 不存在指向NULL的引用,指针可以指向NULL ;
    8. 指针在使用前需要检查合法性,引用不需要;
    9. 可以定义指针数组、不可以定义引用数组;
    int a = 10,b = 20;
    int *arr[2] = {&a, &b} 	//正确
    ------------------------------------
    int &arr[2] = {a, b}	//错误
    
    • 1
    • 2
    • 3
    • 4
    1. 可以定义数组指针,也可以定义数组引用
    int arr[2][2] = {10,20,30,40};
    int (*arr_p)[2] = arr;
    -----------------------------------
    int arr[2] = {10,20};
    int (&arr_p)[2] = arr;//错误的数组引用
    int (&arr_p)[2][2] = arr;//正确的数组引用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 可以定义指针函数,也可以定义引用函数
    int *func_p(int a, int b){}
    -----------------------------------
    int &func_p(int a, int b){}
    
    • 1
    • 2
    • 3
    1. 可以定义函数指针,也可以定义函数引用
    int func(int a, int b){}
    int (*func_p)(int a, int b);
    func_p = func;
    ------------------------------------
    int (&func_r)(int a, int b) = func;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 可以定义指针的指针(二级指针) 不可以定义引用的引用(二级引用)
    int a = 100;
    int *p = &a;
    int **pp = &p;
    ---------------------------------
    int a= 100;
    int &r = a;
    int &&rr = r;  //错误
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    1.2 三维场景动态生成正射纹理-人机交互实现区域框选
    系统运行缓慢,CPU 100%,以及Full GC次数过多问题的排查思路
    【POJ No. 1190】 生日蛋糕
    Spring后处理器-BeanPostProcessor
    混合与剔除
    [LeetCode周赛复盘] 第 299 场周赛20220626
    在安全数字包裹机制下,汽车制造业如何安全可控地实现上下游数据协作?
    【Excel函数】Trim函数删除前后多余的空格
    Pytest----caplog的应用场景以及使用方法
    Simulink|电动汽车、永磁电动机建模与仿真
  • 原文地址:https://blog.csdn.net/m0_65835264/article/details/126547919