一、引用
1.1 引用的概念和定義
引用不是新定義一個變量,而是給已經存在的變量取別名,編譯器不會為引用變量開辟內存空間,它和它引用的變量共用一塊內存空間。
類型&? 引用別名? =? 引用對象
int a = 10;int& b = a; //b是a的引用
1.2 引用的特性
①引用在定義時必須初始化
②一個變量可以有多個引用
③引用一旦引用一個實體,就不能再引用其他實體
注意:引用不能改變指向,所以在鏈式結構中,引用無法代替指針
1.3 引用的使用
引用在實踐中主要是用于引用傳參和引用作為返回值中減少拷貝提高效率以及改變引用對象時同時改變被引用對象。引用傳參和指針傳參功能是類似的,引用傳參相對更方便一些。當引用做函數形參時,修改形參會影響實參。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;void swap(int* px, int* py)
{int tmp = *px;*px = *py;*py = tmp;
}void swap(int& px, int& py)
{int tmp = px;px = py;py = tmp;
}int main()
{int x = 1, y = 2;swap(&x, &y);swap(x, y);return 0;
}
引用作為返回值的場景比較復雜,這里簡單講一下使用場景,還有一些內容在后續的類與對象章節中會繼續深入講解。
首先我們需要知道一個知識,當函數傳值返回時,其實是拷貝一份臨時變量,然后把這個臨時變量的值賦給其他變量,而這個臨時變量具有常性(相當于被const修飾)比如現在有一數組,我們想把數組中每一個值都加1,那么在傳值返回的值的基礎上直接加1是編譯不通過的,因為拷貝的臨時變量具有常性,是不能被修改的,但是當我們傳引用返回時就可以編譯通過了。
因此,引用做返回值類型,一是可以修改返回對象,二是減少拷貝、提高效率。
既然引用有這么多的好處,那是否所有情況都可以把引用作為返回值類型呢?答案是不能!比如下面這段(偽)代碼:
int& func()
{int ret = 0;//...return ret;
}
這個代碼很不安全(因為ret的別名是不安全的)ret是局部對象,當func函數結束,ret就銷毀了,這時返回ret的別名本質上是一種類似訪問野指針的行為。如下圖所示:
如果我們在main函數中用引用去接收這個函數的返回值,就會明顯的出現異常。可以看到,我們并沒有修改x的值,但是x的值從0變成了1。如下圖:
那為什么之前修改數組元素可以傳引用返回呢?因為我們用malloc在堆上申請了一塊空間,并沒有free掉,函數調用結束后這個數組一直都在。
1.4 const引用
C++中,可以引用一個const對象,但是必須要用const引用。const引用也可以引用普通對象,因為對象的訪問權限在引用過程中可以縮小,但是不能放大。
int main()
{int x = 0;int& r1 = x;//權限可以縮小const int& r3 = x;const int y = 1;//權限不能放大//int& r2 = y;const int& r2 = y;const int* p1 = &y;//權限不能放大//int* p2 = p1;const int* p2 = p1;
}
需要注意的是,類似int& rb = a *?3;double d = 3.14,int& rd = d這樣一些情況,a * 3的結果保存在一個臨時變量中,在類型轉換過程中也會產生臨時對象存儲中間值,也就是說,rb和rd引用的都是臨時對象,而我們上面說了,C++中規定臨時對象具有常性,所以這里觸發了權限放大,必須要常引用才可以。
const int& r4 = x * 3;double d = 3.14;const int& r5 = d;
1.5 指針和引用的關系
值得一提的是,在指令匯編角度上,引用是用指針實現的~
二、內聯函數
在C語言階段學過宏函數,編譯器在預處理階段替換,因此不用建立棧幀,本質上能夠提效。但是,宏函數的缺點也很明顯,宏函數的實現很復雜容易出錯,并且不能調試。于是,C++設計了inline來替代C的宏函數。
用關鍵詞inline修飾的函數叫做內聯函數,編譯時C++編譯器會在調用的地方展開內聯函數。inline對于編譯器來說只是一個建議,也就是說,加了inline編譯器也可以選擇在調用的地方不展開,不同編譯器關于什么時候展開的情況不同。inline適用于頻繁調用的短小函數,對于遞歸函數或者代碼相對多一些的函數,加上inline也會被編譯器忽略。
inline int Add(int x, int y)
{return x + y;
}int main()
{int ret = Add(1, 2);cout << ret << endl;return 0;
}
注:inline不建議聲明和定義分離到兩個文件,分離會導致連接錯誤。