C++引用和指針的區別你知道嗎

引用

1.引用概念

引用不是新定義一個變量,而是給已存在變量取瞭一個別名,編譯器不會為引用變量開辟內存空間,它和它引用的變量共用同一塊內存空間。

比如:李逵,在傢稱為"鐵牛",江湖上人稱"黑旋風

2.格式

類型& 引用變量名(對象名) = 引用實體;

例:

void TestRef()
{
int a = 10;
int& ra = a;//<====定義引用類型
printf("%p\n", &a);
printf("%p\n", &ra);
}

在這裡插入圖片描述

註意:引用類型必須和引用實體是同種類型的

3.引用特性

引用在定義時必須初始化一個變量可以有多個引用引用一旦引用一個實體,再不能引用其他實體

例:

在這裡插入圖片描述

引用實例:

在這裡插入圖片描述

4.常引用

1.const引用

eg1:

在這裡插入圖片描述

const引用在傳參引用時的意義:

1.可以保護形參返回不會改變實參的值(函數傳參如果想減少拷貝而用瞭引用傳參,如果函數中不改變這個參數,最好用const引用傳參)

2.即可接受變量,也可接受常量。

eg2:

在這裡插入圖片描述

5.使用場景

1、引用作為參數

引用的一個重要作用就是作為函數的參數。以前的C語言中函數參數傳遞是值傳遞,如果有大塊數據作為參數傳遞的時候,采用的方案往往是指針,因為 這樣可以避免將整塊數據全部壓棧,可以提高程序的效率。但是現在(C++中)又增加瞭一種同樣有效率的選擇(在某些特殊情況下又是必須的選擇),就是引 用。

[例]:

void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
int main()
{
 int a,b;
      cin>>a>>b; //輸入a,b兩變量的值
      swap(a,b); //直接以變量a和b作為實參調用swap函數
      cout<<a<< ' ' <<b; //輸出結果

}

在這裡插入圖片描述

由【例】可看出:

(1)傳遞引用給函數與傳遞指針的效果是一樣的。這時,被調函數的形參就成為原來主調函數中的實參變量或對象的一個別名來使用,所以在被調函數中對形參變量的操作就是對其相應的目標對象(在主調函數中)的操作。

(2)使用引用傳遞函數的參數,在內存中並沒有產生實參的副本,它是直接對實參操作;而使用一般變量傳遞函數的參數,當發生函數調用時,需要給 形參分配存儲單元,形參變量是實參變量的副本;如果傳遞的是對象,還將調用拷貝構造函數。因此,當參數傳遞的數據較大時,用引用比用一般變量傳遞參數的效 率和所占空間都好。

(3)使用指針作為函數的參數雖然也能達到與使用引用的效果,但是,在被調函數中同樣要給形參分配存儲單元,且需要重復使用"*指針變量名"的 形式進行運算,這很容易產生錯誤且程序的閱讀性較差;另一方面,在主調函數的調用點處,必須用變量的地址作為實參。而引用更容易使用,更清晰。

2. 引用作為做返回值

下面代碼輸出什麼結果?為什麼?

int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int ret = Add(1, 2);
Add(3, 4);
cout << "Add(1, 2) is :"<< ret <<endl;
return 0;
}

在這裡插入圖片描述

對於以上代碼,是將c的別名傳給瞭ret,結果確不確定,取決於平臺銷毀棧幀時,是否清理棧幀空間(由運行結果知;vs下不清理)那麼我們進一步改動實驗證明引用返回的危害:

int& Add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int &ret = Add(1, 2);
	Add(3, 4);
	cout << "Add(1, 2) is :" << ret << endl;
	return 0;
}

此時相當與retc的別名,再次運行:

在這裡插入圖片描述

結果出錯。

舉個恰當的例子:

在這裡插入圖片描述

因此註意:如果函數返回時,出瞭函數作用域,如果返回對象還未還給系統,則可以使用引用返回,如果已經還給系統瞭,則必須使用傳值返回。

傳值、傳引用效率比較:

以值作為參數或者返回值類型,在傳參和返回期間,函數不會直接傳遞實參或者將變量本身直接返回,而是傳遞實參或者返回變量的一份臨時的拷貝,因此用值作為參數或者返回值類型,效率是非常低下的,尤其是當參數或者返回值類型非常大時,效率就更低。

例:

#include <time.h>
struct A{ int a[10000]; };
void TestFunc1(A a){}
void TestFunc2(A& a){}
void TestRefAndValue()
{
A a;
// 以值作為函數參數
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作為函數參數
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分別計算兩個函數運行結束後的時間
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

在這裡插入圖片描述

值和引用的作為返回值類型的性能比較:

#include <time.h>
struct A{ int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a;}
// 引用返回
A& TestFunc2(){ return a;}
void TestReturnByRefOrValue()
{
// 以值作為函數的返回值類型
size_t begin1 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc1();
size_t end1 = clock();
// 以引用作為函數的返回值類型
size_t begin2 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc2();
size_t end2 = clock();
// 計算兩個函數運算完成之後的時間
cout << "TestFunc1 time:" << end1 - begin1 << endl;
cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

在這裡插入圖片描述

通過上述代碼的比較,發現傳值和指針在作為傳參以及返回值類型上效率相差很大

6.引用和指針的區別

在語法概念上引用就是一個別名,沒有獨立空間,和其引用實體共用同一塊空間 在底層實現上實際是有空間的,因為引用是按照指針方式來實現的:

實例:

int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 30;
return 0;
}

我們來看下引用和指針的匯編代碼對比:

在這裡插入圖片描述

7.引用和指針的不同點:

引用在定義時必須初始化,指針沒有要求

引用在初始化時引用一個實體後,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型 實體沒有NULL引用,但有NULL指針在sizeof中含義不同:引用結果為引用類型的大小,但指針始終是地址空間所占字節個數(32位平臺下占 4個字節)引用自加即引用的實體增加1,指針自加即指針向後偏移一個類型的大小有多級指針,但是沒有多級引用訪問實體方式不同,指針需要顯式解引用,引用編譯器自己處理

引用比指針使用起來相對更安全

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: