理解C++中引用的底層實現

1、C++ Primer提到:引用並非對象,相反的,它只是爲一個已經存在的對象所起的另外一個名字。

引用的定義必須伴隨初始化,而且一旦定義了引用,就無法令其再綁定到另外的對象,之後每次使用這個引用都是訪問它最初綁定的那個對象。

2、何爲對象?
對於面向對象來說,對象就是類的實例,是抽象化的數據本身。
更廣義的來說,一個int型變量可以是對象,一個指針也可以是對象,但一個引用卻又不是對象。可以理解:在語言層面上,佔用內存的變量都可以稱之爲對象。

3、引用佔內存嗎?
在語言層面上:不佔內存!
不信看下面的簡單程序:

void function()
{
    int x = 0;
    int& r = x;
    int* p1 = &x;
    int* p2 = &r;
    std::cout << p1 << std::endl;
    std::cout << p1 << std::endl;
    return 0;
}
輸出:
0x7ffcb64d705c
0x7ffcb64d705c

4、窺視底層呢?
再來看一段代碼:

int function() {
    int x = 0;
    int* ptr = &x;
    int& r = x;
    r = 1;
    return r;
}

彙編代碼:

  pushq %rbp
  movq %rsp, %rbp           //拷貝棧指針
  movl $0, -20(%rbp)        //int x = 0
  leaq -20(%rbp), %rax       
  movq %rax, -8(%rbp)       //int* ptr = &x
  leaq -20(%rbp), %rax
  movq %rax, -16(%rbp)      //int& r = x
  movq -16(%rbp), %rax
  movl $1, (%rax)           //r = 1
  movq -16(%rbp), %rax
  movl (%rax), %eax         
  popq %rbp
  ret                       //return r

這裏寫圖片描述

movl $0, -20(%rbp) 
首先在棧的%rbp-20的地方分配了一個4字節的空間儲存變量x=0;
leaq -20(%rbp), %rax
movq %rax, -8(%rbp)
把x的地址保存到%rbp-8的棧空間處,int* ptr = &x;
leaq -20(%rbp), %rax
movq %rax, -16(%rbp)
把x的地址保存到%rbp-16的棧空間處, int& r = x;
movq -16(%rbp), %rax
movl $1, (%rax)
將%rbp-16棧空間處的值改寫成1, r = 1;
movq -16(%rbp), %rax
movl (%rax), %eax
popq %rbp
ret
將%rbp-16棧空間處的值返回, return r;

思路很清晰了。創建指針和創建引用的彙編代碼幾乎完全一樣,在棧上分配空間,保存變量x的地址,所有對引用變量r的操作實際都是通過這個地址在操作x。
和指針有區別嗎?沒有。
佔用內存嗎?從底層來看,確實佔用了內存。

5、小結
本質上,引用和指針沒有區別。只不過在語言層面上,C++設計者將通過指針來操作引用的實現細節隱藏了。不過,我們依然可以肯定:
(1)定義一個引用就是定義一個指針,這個指針保存引用對象的地址,且指針類型爲const,不可以再指向其他對象;
(2)每次對引用變量的使用,實際都伴隨着解引用,知識我們看不到符號*;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章