C++基礎知識

連接:https://zhuanlan.zhihu.com/p/38399566
本文主要提一下如下三個區別:程序員

  1. 引用必須初始化,而指針能夠不初始化。

咱們在定義一個引用的時候必須爲其指定一個初始值,可是指針卻不須要。安全

int &r;    //不合法,沒有初始化引用
int *p;    //合法,但p爲野指針,使用須要當心

2. 引用不能爲空,而指針能夠爲空。函數

因爲引用不能爲空,因此咱們在使用引用的時候不須要測試其合法性,而在使用指針的時候須要首先判斷指針是否爲空指針,不然可能會引發程序崩潰。性能

void test_p(int* p)
{
  	if(p != nullptr)    //對p所指對象賦值時需先判斷p是否爲空指針
    	*p = 3;
    return;
}
void test_r(int& r)
{
    r = 3;    //因爲引用不能爲空,因此此處無需判斷r的有效性就能夠對r直接賦值
    return;
}

3. 引用不能更換目標測試

指針能夠隨時改變指向,可是引用只能指向初始化時指向的對象,沒法改變。spa

int a = 1;
int b = 2;

int &r = a;    //初始化引用r指向變量a
int *p = &a;   //初始化指針p指向變量a

p = &b;        //指針p指向了變量b
r = b;         //引用r依然指向a,但a的值變成了b

引用的使用場景

只看二者區別的話,咱們發現引用能夠完成的任務均可以使用指針完成,而且在使用引用時限制條件更多,那麼C++爲何要引入「引用」呢?指針

限制條件多不必定是缺點,C++的引用在減小了程序員自由度的同時提高了內存操做的安全性和語義的優美性。好比引用強制要求必須初始化,可讓咱們在使用引用的時候不用再去判斷引用是否爲空,讓代碼更加簡潔優美,避免了指針滿天飛的情形。除了這種場景以外引用還用於以下兩個場景:code

  1. 引用型參數

通常咱們使用const reference參數做爲只讀形參,這種狀況下既能夠避免參數拷貝還能夠得到與傳值參數同樣的調用方式。對象

void test(const vector<int> &data)
{
    //...
}
int main()
{
    vector<int> data{1,2,3,4,5,6,7,8};
    test(data);
}

2. 引用型返回值內存

C++提供了重載運算符的功能,咱們在重載某些操做符的時候,使用引用型返回值能夠得到跟該操做符原來語法相同的調用方式,保持了操做符語義的一致性。一個例子就是operator []操做符,這個操做符通常須要返回一個引用對象,才能正確的被修改。

vector<int> v(10);
v[5] = 10;    //[]操做符返回引用,而後vector對應元素才能被修改
              //若是[]操做符不返回引用而是指針的話,賦值語句則須要這樣寫
*v[5] = 10;   //這種書寫方式,徹底不符合咱們對[]調用的認知,容易產生誤解

指針與引用的性能差距

指針與引用之間有沒有性能差距呢?這種問題就須要進入彙編層面去看一下。咱們先寫一個test1函數,參數傳遞使用指針:

void test1(int* p)
{
    *p = 3;    //此處應該首先判斷p是否爲空,爲了測試的須要,此處咱們沒加。
    return;
}

該代碼段對應的彙編代碼以下:

pushq	%rbp
movq	%rsp, %rbp
movq	%rdi, -8(%rbp)
movq	-8(%rbp), %rax
movl	$3, (%rax)
nop
popq	%rbp
ret

上述代碼一、2行是參數調用保存現場操做;第3行是參數傳遞,函數調用第一個參數通常放在rdi寄存器,此行代碼把rdi寄存器值(指針p的值)寫入棧中;第4行是把棧中p的值寫入rax寄存器;第5行是把當即數3寫入到rax寄存器值所指向的內存中,此處要注意(%rax)兩邊的括號,這個括號並並非無關緊要的,(%rax)和%rax徹底是兩種意義,(%rax)表明rax寄存器中值所表明地址部分的內存,即至關於C++代碼中的*p,而%rax表明rax寄存器,至關於C++代碼中的p值,因此彙編這裏使用了(%rax)而不是%rax。

咱們再寫出參數傳遞使用引用的C++代碼段test2:

void test2(int& r)
{
    r = 3;    //賦值前無需判斷reference是否爲空
    return;
}

這段代碼對應的彙編代碼以下:

pushq	%rbp
movq	%rsp, %rbp
movq	%rdi, -8(%rbp)
movq	-8(%rbp), %rax
movl	$3, (%rax)
nop
popq	%rbp
ret

咱們發現test2對應的彙編代碼和test1對應的彙編代碼徹底相同,這說明C++編譯器在編譯程序的時候將指針和引用編譯成了徹底同樣的機器碼。因此C++中的引用只是C++對指針操做的一個「語法糖」,在底層實現時C++編譯器實現這兩種操做的方法徹底相同。

總結

C++中引入了引用操做,在對引用的使用加了更多限制條件的狀況下,保證了引用使用的安全性和便捷性,還能夠保持代碼的優雅性。在適合的狀況使用適合的操做,引用的使用能夠必定程度避免「指針滿天飛」的狀況,對於提高程序魯棒性也有必定的積極意義。最後,指針與引用底層在實現大部分狀況下都是同樣的,不用擔憂二者的性能差距。

相關文章
相關標籤/搜索