• C++引用的实现


    当我学习C++引用时,听到的第一句话是“引用是变量的别名,不像指针一样需要占用内存空间”。然而学到深处,发现此话并不完全正确。

    本文主要介绍我如何通过实验来了解到C++引用的实现,其实引用的内部就是指针。当然这也于编译器有关,所以这里需要提及一下测试所用的编译器及环境。

    测试环境是MinGW的g++ 8.1.0,64位编译器,64位的机子。所以指针的大小是8个字节,即64个bit。(注:因为目的是测试,所以测试时并没有处理对new操作符所产生对象的回收)

    首先我写出了如下代码,试图通过指针偏移来获取有关引用的信息:

    #include <iostream>
    #include <string>
    using namespace std;
    
    int main() {
        int64_t x;
        string& str = *new string();
        int64_t y;
    
        cin >> str; // 对引用做一次操作,避免编译器把变量优化掉
    
        cout << &x << endl;
        cout << &y << endl;
        cout << str << endl;
    
        return 0;
    }
    

    然而,这个程序的输出如下(str的输出忽略):

    0x61fe00
    0x61fdf8
    

    难道引用真的不占内存?编译器真的很聪明,可能优化掉了吧;经过一系列尝试,我写出了另外一段代码:

    #include <iostream>
    #include <string>
    using namespace std;
    
    void foo(int64_t q, string& s, int64_t r) {
        cout << "&q: " << &q << endl;
        cout << "&r: " << &r << endl;
    
        cout << "*(string**)(&q + 1): " << *(string**)(&q + 1) << endl;
    }
    
    int main()
    {
        string& str = *new string();
        cout << "main(): " << &str << endl;
        foo(0, str, 0);
        return 0;
    }
    

    这段代码的输出是:

    main(): 0x1e1bd0
    &q: 0x61fde0
    &r: 0x61fdf0
    *(string**)(&q + 1): 0x1e1bd0
    

    可见,q的地址是0x61fde0,r的地址是0x61fdf0。两个地址间相差16个字节!这里引用占用的内存出来了。显然引用对应的指针存储在q的8个字节之后。我们可以将q的地址加1,也就是加上8个字节,这里存储的就是引用的信息。假设它就是指针,那么考虑:(&q + 1)本身是一个指向string*的指针,也就是string**。所以若要获取指针的值,需要对这个值解一次引用,输出出来。(当然如果你想简单一点,可以直接把它转成int64_t然后用16进制输出亦可)

    至此真相大白,程序输出的最后一行0x1e1bd0与主函数中new出来的对象的地址(见输出第一行)一致。所以得出结论:引用是用指针实现的。用户对引用的访问操作都内含一次解引用,而这对用户来说是透明的

    不过需要提及的是,回想本文的第一个测试,发现引用的指针空间被优化掉了。所以引用有时也不一定会在栈上真正以指针体现出来。

  • 相关阅读:
    Redis-其他命令
    Redis-发布与订阅
    C#使用命令编译代码
    Redis有序集合操作
    Redis散列操作
    设置ul水平居中
    Redis集合操作
    Redis列表操作
    java连SQLServer失败 java.lang.ClassNotFoundException:以及 javax.xml.bind.JAXBException
    SQLServer 用法简例
  • 原文地址:https://www.cnblogs.com/sandychn/p/12436069.html
Copyright © 2020-2023  润新知