zoukankan      html  css  js  c++  java
  • C++解析(4):引用的本质

    0.目录

    1.引用的意义

    2.特殊的引用

    3.引用的本质

    4.函数返回引用

    5.小结

    1.引用的意义

    • 引用作为变量別名而存在,因此在一些场合可以代替指针
    • 引用相对于指针来说具有更好的可读性实用性


    注意:函数中的引用形参不需要进行初始化!!!

    2.特殊的引用

    const引用:

    • 在C++中可以声明const引用
    • const Type& name = var;
    • const引用让变量拥有只读属性

    当使用常量const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名。

    结论:使用常量对const引用初始化后将生成一个只读变量!!!
    (引用只能是另一个变量的别名,因此一个引用绝对不可能是一个常量值的别名,换句话说,不能定义一个引用,然后用一个常量对它进行初始化,这里的常量是字面常量。但是有一个例外,就是const引用。我们可以使用字面常量来对const引用进行初始化,编译器在这个时候会真正的产生一个只读变量。由于是只读变量,所以想要直接对它进行赋值肯定会编译出错,但是依旧可以通过指针来改变只读变量的值。)

    引用的特殊意义:在C++中,想要一个使已经存在的变量拥有只读属性,变成一个只读变量,只需要定义一个const引用即可。

    思考:引用有自己的存储空间吗?

    运行下列程序:

    #include <stdio.h>
    
    struct TRef
    {
        char& r;
    };
    
    int main()
    {
        char c = 'c';
        char& rc = c;
        TRef ref = { c };
        
        printf("sizeof(char&) = %d
    ", sizeof(char&));
        printf("sizeof(rc) = %d
    ", sizeof(rc));
        
        printf("sizeof(TRef) = %d
    ", sizeof(TRef));
        printf("sizeof(ref.r) = %d
    ", sizeof(ref.r));
        
        return 0;
    }
    

    输出结果如下:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    sizeof(char&) = 1
    sizeof(rc) = 1
    sizeof(TRef) = 8
    sizeof(ref.r) = 1
    

    (引用所占用的内存空间的大小与指针相同!)

    3.引用的本质

    引用在C++中的内部实现是一个指针常量

    注意:

    1. C++编译器在编译过程中指针常量作为引用的内部实现,因此引用所占用的空间大小和指针相同
    2. 从使用的角度引用只是一个别名C++为了是实用性而隐藏了引用的存储空间这一细节

    在C++编译器内部是用指针实现了引用的概念:

    #include <stdio.h>
    
    struct TRef
    {
        char* before;
        char& ref;
        char* after;
    };
    
    int main()
    {
        char a = 'a';
        char& b = a;
        char c = 'c';
        
        TRef r = {&a, b, &c};
        
        printf("sizeof(r) = %d
    ", sizeof(r));
        printf("sizeof(r.before) = %d
    ", sizeof(r.before));
        printf("sizeof(r.after) = %d
    ", sizeof(r.after));
        printf("&r.before = %p
    ", &r.before);
        printf("&r.after = %p
    ", &r.after);
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    sizeof(r) = 24
    sizeof(r.before) = 8
    sizeof(r.after) = 8
    &r.before = 0x7ffd9d27af40
    &r.after = 0x7ffd9d27af50
    

    (在汇编层次,指针和引用的操作是一样的。)

    C++中的引用旨在大多数的情况下代替指针

    • 功能性:可以满足多数需要使用指针的场合
    • 安全性:可以避开由于指针操作不当而带来的内存错误
    • 操作性:简单易用,又不失功能强大

    4.函数返回引用

    分析下面的代码:

    #include <stdio.h>
    
    int& demo()    // int* const
    {
        int d = 0;
        
        printf("demo: d = %d
    ", d);
        
        return d;  // return &d
    }
    
    int& func()
    {
        static int s = 0;
        
        printf("func: s = %d
    ", s);
        
        return s;  // return &s
    }
    
    int main()
    {
        int& rd = demo();
        int& rs = func();
        
        printf("
    ");
        printf("main: rd = %d
    ", rd);
        printf("main: rs = %d
    ", rs);
        printf("
    ");
        
        rd = 10;
        rs = 11;
        
        demo();
        func();
        
        printf("
    ");
        printf("main: rd = %d
    ", rd);
        printf("main: rs = %d
    ", rs);
        printf("
    ");
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    test.cpp: In function ‘int& demo()’:
    test.cpp:5: warning: reference to local variable ‘d’ returned
    [root@bogon Desktop]# ./a.out 
    demo: d = 0
    func: s = 0
    
    main: rd = 53
    main: rs = 0
    
    demo: d = 0
    func: s = 11
    
    main: rd = 53
    main: rs = 11
    
    

    demo()函数:
    返回了一个局部变量的引用,其实返回了一个局部变量的地址。切记:在使用引用时,我们不用返回局部变量的引用。

    func()函数:
    依旧想要返回一个局部变量的引用,但是这个局部变量是静态的。因为静态的局部变量它的存储空间是一个全局的存储区,所以它所对应的空间不会由于函数调用的返回而被摧毁,因此这样的做法是没有问题的。

    程序编译会有警告(warning):不要返回局部变量的引用。

    main函数内部:
    rd所代表的变量已经在demo函数返回的时候被摧毁了,这时候rd代表的已经是一个不存在的变量了,从指针的角度来看,这时候rd已经相当于一个野指针了。第31行rd想要对栈上已经被释放的4个字节赋值为10,不但没意义,而且很危险。

    结论:
    这个例子想告诉大家:引用能在最大的程度上去避开内存操作的错误,但是不能寄希望于有了引用就可以乱来,引用不能完全避免内存方面的错误,原因是引用的本质就是指针。只要它还是指针,必然多多少少还可能遇见指针方面的操作问题,以上就是其中一个最典型的问题。

    5.小结

    • 引用作为变量别名而存在旨在代替指针
    • const引用可以使得变量具有只读属性
    • 引用在编译器内部使用指针常量实现
    • 引用的最终本质为指针
    • 引用可以尽可能的避开内存错误
  • 相关阅读:
    121. Best Time to Buy and Sell Stock
    70. Climbing Stairs
    647. Palindromic Substrings
    609. Find Duplicate File in System
    583. Delete Operation for Two Strings
    556 Next Greater Element III
    553. Optimal Division
    539. Minimum Time Difference
    537. Complex Number Multiplication
    227. Basic Calculator II
  • 原文地址:https://www.cnblogs.com/PyLearn/p/10071492.html
Copyright © 2011-2022 走看看