
你好,我是安然無虞。 |
文章目錄
- 學習網站
- 寫在前面
- 引用概念
- 引用特性
- 常引用
- 引用使用場景
- · 做參數
- · 做傳回值
- 傳值、傳引用效率比較
- 引用和指針的差別
- 大廠面試真題
學習網站
推薦給老鐵們兩款學習網站:
面試利器&算法學習:牛客網 風趣幽默的學人工智能:人工智能學習 首個付費專欄:《C++入門核心技術》
寫在前面
前面我們有講解函數重載相關的知識,因為比較重要,是以講的比較詳細,下面還有一個很重要的知識點——引用,雖然很重要但是不難哦,不信你看看就知道了。
引用概念
引用不是新定義一個變量,而是給已存在變量取一個别名,編譯器不會為引用變量開辟記憶體空間,它和它引用的變量共用同一塊記憶體空間。
比如:我們知道,在水浒傳中有一個好漢名叫李逵,他在家呢叫“鐵牛”,江湖人稱“黑旋風”。
有點小可愛哦。
基本結構:
類型& 引用變量名 = 引用實體;
注意哦,引用類型必須是和引用實體是同種類型的。
看下面一段代碼:
#include<iostream>
using std::cout;
using std::endl;
int main()
{
int a = 10;
int& b = a;//b是a的引用(别名)
int& c = a;//c是a的引用(别名)
int& d = b;//d是b的引用(别名)
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
cout << "d = " << d << endl;
return 0;
}
前面引用的概念已經說了,引用不是新定義一個變量,而是給已存在變量取了一個别名,編譯器不會為引用變量開辟記憶體空間,它和它引用的變量共用同一塊空間。
這樣的話也就是說,變量a、b、c、d共用同一塊空間。
調試代碼驗證一下上面的說法,看四個變量的位址:
很明顯位址一樣,是以說,引用不是新定義一個變量,而是給已存在的變量取一個别名,編譯器不會為引用變量開辟記憶體空間,它和它引用的變量共用同一塊記憶體空間。
引用特性
- 引用在定義時必須初始化;
- 一個變量可以有多個引用;
- 引用一旦引用一個實體,不能再引用其他實體。
對上述三個特性一一說明:
1.引用在定義時必須初始化
void Test1()
{
int a = 10;
int& ra; //這條語句編譯時會出錯
int& rb = a; //引用在定義時必須初始化
}
2.一個變量可以有多個引用
void Test2()
{
int a = 10;
int& ra = a;
int& rb = a;
}
3.引用一旦引用一個實體,再不能引用其他實體(注意哦,這點跟指針不同)
void Test3()
{
int a = 10;
int b = 20;
int& ra = a;
int& ra = b;//編譯錯誤
}
常引用
開始之前呢,先說一下取别名的原則:
對原引用變量,讀寫權限要麼不變,要麼縮小,但是不能放大。
void TestConstRef()
{
const int a = 10;//常變量,隻讀不可寫
//int& ra = a;編譯報錯,a->ra 權限放大(a隻讀,ra可讀可寫)
const int& ra = a;//a->ra 權限不變
//int& b = 10;編譯報錯,常量10->a 權限放大
const int& b = 10;//10->b 權限不變
double d = 3.14;
//int& rd = d;編譯出錯,double->int 類型不同
const int& rd = d;//編譯通過,想想為什麼?
}
下面這段代碼:
double d = 3.14;
//int& rd = d;編譯出錯,double->int 類型不同
const int& rd = d;//編譯通過,想想為什麼?
為什麼加上const就可以了呢?
不着急,先說說之前學習過的知識。我們在學習C語言的時候知道不同類型的值互相指派時會發生隐式類型的轉換,比如:
double d = 3.14;
int f = d;
很明顯,發生隐式類型轉換時中間會産生一個臨時變量,而臨時變量具有常性。
知道這一點上之後,就好解釋這段代碼了:
double d = 3.14;
//int& rd = d;編譯出錯,double->int 類型不同
const int& rd = d;//編譯通過,想想為什麼?
上面有說到rd不是d的别名,而是臨時變量的别名,下面我們調試驗證:
引用使用場景
· 做參數
之前我們寫交換函數是這樣寫的:
void Swap(int* left, int* right)
{
int temp = *left;
*left = *right;
*right = temp;
}
現在我們學習引用後,大可不必像之前那樣:
void swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
使用引用傳參的好處:
- 無需解引用,簡單多了;
- 使用引用傳參更直覺,更好了解。
· 做傳回值
我們知道,傳值傳回時會有一個拷貝,而傳引用傳回就沒有這個拷貝,傳回的直接就是變量的别名,這樣可以減少拷貝,提高效率。
思考下面一段代碼:
int& Count()
{
static int n = 0;//注意靜态變量的特點:隻會初始化一次
n++;
return n;
}
int main()
{
int& ret = Count();
return 0;
}
傳引用傳回的是傳回值的别名,傳值傳回的是那個臨時變量。
是以這裡傳回的是n的别名,為了證明這個說法,隻需要驗證ret 和 n的位址一樣,意味着ret 就是 n 的别名。
好,下面進行調試驗證:
故而證明上述結論:
傳引用傳回的是傳回值的别名,傳值傳回的是那個臨時變量。
好的,現在将代碼改動一點:
int Count()
{
static int n = 0;
n++;
return n;
}
int main()
{
int ret = Count();
return 0;
}
我們知道,傳值傳回的是那個臨時變量,那麼如何證明産生臨時變量了呢?很簡單,不信你看:
int& ret = Count();//報錯
其實不難,不過需要你細品。
再看下面一段代碼,問輸出結果是什麼?為什麼?
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;
}
其實這裡的 Add 函數是錯誤的,因為 c 是局部變量,出了作用域就被銷毀了,是以不能使用引用傳參,會導緻野指針。
注意:函數傳回時,出了函數作用域,如果傳回對象還未還給系統,則可以使用引用傳回,如果已經還給系統了,則必須使用傳值傳回。
傳值、傳引用效率比較
以值作為參數或者傳回值類型,在傳參和傳回期間,函數不會直接傳遞實參或者将變量本身直接傳回,而是傳遞實參或者傳回變量的一份臨時拷貝,是以用值作為參數或者傳回值類型,效率是非常低下的,尤其是當參數或者傳回值類型非常大時,效率就更低。
引用和指針的差別
在文法概念上引用就是一個别名,沒有獨立的空間,和其引用的實體共用同一塊空間。
int a = 10;
//文法角度而言,ra是a的别名,沒有額外開空間
int& ra = a;
//文法角度而言,pa存儲a的位址,pa開了4/8個位元組的空間
int* pa = &a;
不過在底層實作上其實是有空間的,因為引用是按照指針方式來實作的。看彙編代碼即可證明:
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
我們來看一下引用和指針的彙編代碼對比:
看到了嗎,一模模一樣!
總結引用和指針的不同點:
- 引用在定義時必須初始化,指針沒有要求;
- 引用在初始化時引用一個實體後,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型的實體;
- 沒有NULL引用,但有NULL指針;
- 在sizeof中含義不同:引用結果為引用類型的大小,但指針始終是位址空間所占位元組個數(32位平台占4個位元組,64位平台占8個位元組);
- 引用自加即引用的實體增加1,指針自加即指針向後偏移一個類型的大小;
- 有多級指針,但是沒有多級引用;
- 通路實體方式不同,指針需要顯示解引用,引用是編譯器自己處理;
- 引用比指針使用起來相對更加安全。
上面這8點,不要死記硬背,要了解性記憶哦!
大廠面試真題
1、關于引用以下說法錯誤的是( )。(阿裡)
A.引用必須初始化,指針不必
B.引用初始化以後不能被改變,指針可以改變所指的對象
C.不存在指向空值的引用,但是存在指向空值的指針
D.一個引用可以看作是某個變量的一個“别名”
E.引用傳值,指針傳位址
F.函數參數可以聲明為引用或指針類型
解析:引用表面好像是傳值,其本質也是傳位址,隻是這個工作有編譯器來做,是以E錯。
2、引用”與指針的差別是什麼( )
A.指針通過某個指針變量指向一個對象後,對它所指向的變量間接操作。程式中使用指針,程式的可讀性差;而引用本身就是目标變量的别名,對引用的操作就是對目标變量的操作
B.引用通過某個引用變量指向一個對象後,對它所指向的變量間接操作。程式中使用引用,程式的可讀性差;而指針本身就是目标變量的别名,對指針的操作就是對目标變量的操作
C.指針比引用更節省存儲空間
D.以上都不正确
解析:指針是間接操作對象,引用時對象的别名,對别名的操作就是對真實對象的直接操作;指針需要開辟空間,引用不需要開辟空間。
3、關于引用與指針的差別,下面叙述錯誤的是( )
A.引用必須被初始化,指針不必
B.指針初始化以後不能被改變,引用可以改變所指的對象
C.删除空指針是無害的,不能删除引用
D.不存在指向空值的引用,但是存在指向空值的指針
解析:空指針沒有任何指向,删除無害,引用是别名,删除引用就删除真實對象。