Declares a named variable as a reference, that is, an alias to an already-existing object or function. 來自: C++參考手冊
先從一個簡單的例子開始: 交換函數 swap() 。c++
// 代碼片斷01 void swap01(int a, int b) { int temp = a; a = b; b = temp; } // 代碼片斷02 void swap02(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } int main(int argc, const char * argv[]) { int a= 10, b = 20; swap01(a, b); // 不會交換 swap02(&a, &b); // 會交換 return 0; }
上述代碼彷佛不用解釋都知道它所要說明的問題。彷佛與今天的主題無關,可是不着急,看下 代碼片斷02 。內部充斥着 *a
、*b
這樣的操做,代碼彷佛不夠簡潔。有沒有什麼方式可使其更簡潔一些呢?有! 引用!!
。程序員
有了引用之後,代碼是這樣的:數組
void swap03(int &a, int &b) { int temp = a; a = b; b = temp; } int main(int argc, const char * argv[]) { int a= 10, b = 20; swap03(a, b); // 會交換 return 0; }
代碼看起來和上面沒有什麼大的差異。&a
、&b
彷佛是!在取地址。實際上不是的。它表示的 a
是某個 int
類型變量的引用(或者說某個 int
類型變量的別名)。函數
那麼如何建立一個 引用
呢?spa
int main(int argc, const char * argv[]) { int num = 10; int & ref = num; // 建立一個引用。 int & refref = ref; // 建立一個引用 return 0; }
如上,建立的是 num
的引用,名字叫 ret
。既然是引用,就是說,使用 num
和 ret
沒有任何區別。 設計
沒有任何區別是什麼意思?意思就是說:num
和 ret
都是同一個內存地址的名稱。舉個小例子,如圖:指針
num: 周樟壽 ref: 周樹人 refref: 魯迅
只是名字變了,但它都是同一個內存!!code
說到這裏,有沒有發現其實引用是什麼?像不像下面這樣:對象
int &ref = num; // 等價於 int *ref = #
另外有一個須要注意的地方是: 引用一旦建立,便不能更改(只讀)!!既然不能修改,那就說明必須在建立時就要給引用初始化!!!。blog
int one = 10; int two = 20; // 錯誤01(ref建立時就要被初始化) int &ref; ref = one; // 正確寫法:int &ref = one; // 異常 02(引用一旦建立,便不能修改) int &ref = one; ref = two; // 這句話不會報錯,可是它表達的意思不是讓ref變成two的引用。而是將 two 的值,賦值給 ref。即,ref = 20;
至此,對引用有了一個基本的認識。總結一下:
基於以上的觀點。咱們能夠對引用有一個本質上的認識,即指針常量:
int & ref = num; // 等價於 int * const ref = #
引用難道只是爲了簡化
*a
這樣的代碼,使代碼更簡潔麼?
不是的。引用來源於指針,但不一樣於指針。
引用最主要的用途是:用做函數的參數(尤爲是結構體或對象參數)!!
做爲函數參數
struct Student { std::string id; std::string name; int age; }; void func01(Student student) { // do something } void func02(Student &student) { // do something }
【注意】 使用引用做爲參數時,常加上 const
修飾。理由以下:
避免無心中修改被引用的數據,如:
int i = 5; int cube(int &a) { a *= a * a; return a; } int sum(int &a) { a = a + 5; return a; } int tmp1 = cube(i); // 輸入時i= 5,輸出時i = 125 int tmp2 = sum(i); // 輸入時i = 125,輸出時i = 130
可以處理 const
和 非const
實參,不然只能接受 非const
數據,如:
const int NUM = 100; int sum01(int &a) { // do something return 0; } int sum02(const int &a) { // do something return 0; } sum01(NUM);// ERROR!!!! sum02(NUM);// OK
做爲函數返回值
返回值是引用的函數,實際上返回的是被引用變量的「別名」
有一個須要注意的地方是:不要返回臨時變量。以下:
Student &clone(const Student &s) { Student student; // 局部變量,函數執行完成時會被銷燬。 student.id = s.id; student.name = s.name; student.age = s.age; return student; }
如上,會返回 student。可是 student 是局部的臨時變量,在 clone 函數執行完成後,將被銷燬,致使程序出現異常。避免這種問題有一些作法:
返回一個做爲參數傳遞給函數的引用。如:
Student &clone(const Student &s) { return s; }
用 new
來分配新的內存空間, 如:
Student &clone(const Student &s) { Student *student = new Student(); student->id = s.id; student->name = s.name; student->age = s.age; return *student; }
使用引用參數的主要緣由有兩個:
- 程序員可以修改調用函數中的數據對象。
- (最主要)經過傳遞引用而不是整個數據對象(如:結構體和類對象這種數據對象較大的對象),能夠提升程序的運行速度。
正如前面所說,引用來源於指針,但不一樣於指針。甚至引用像是指針的 "語法糖" 。那麼問題來了,何時使用引用,何時使用指針,何時使用按值傳遞呢???
對於使用傳遞的值而不做修改的函數
- 若是數據對象比較小(如:基本數據類型或者小型的結構),選擇按值傳遞
- 若是數據對象是數組,則使用指針,由於這是惟一的選擇,並將指針聲明爲指向
const
的指針- 若是數據對象是較大的結構,則使用指針或者引用,以提升程序的效率。這樣能夠節省複製結構所需的時間和空間
- 若是數據對象是類對象,則使用
const
修飾的引用。類設計的語義經常要求使用語義,這是C++新增這項特性的主要緣由。所以傳遞類對象參數的標準方式是按引用傳遞。對於修改調用函數中數據的函數
- 若是數據對象是基本數據類型,則使用指針。
- 若是數據對象是數組,則只能使用指針。
- 若是數據對象是結構,則使用指針或者引用。
- 若是數據對象是類對象,則使用引用。