1. 引用基本用法
引用是c++對c的重要擴充。在c/c++中指針的作用基本都是一樣的,但是c++增加了另外一種給函數傳遞地址的途徑,這就是按引用傳遞(pass-by-reference),它也存在於其他一些編程語言中,並不是c++的發明。
變量名實質上是一段連續內存空間的別名,是一個標號(門牌號)
程序中通過變量來申請並命名內存空間
通過變量的名字可以使用存儲空間
對一段連續的內存空間只能取一個別名嗎? c++中新增了引用的概念,引用可以作為一個已定義變量的別名。 |
基本語法:
Type& ref = val; |
注意事項:
&在此不是求地址運算,而是起標識作用。
類型標識符是指目標變量的類型
必須在聲明引用變量時進行初始化。
引用初始化之后不能改變。
不能有NULL引用。必須確保引用是和一塊合法的存儲單元關聯。
可以建立對數組的引用。
//1. 認識引用 void test01(){ int a = 10; //給變量a取一個別名b int& b = a; cout << "a:" << a << endl; cout << "b:" << b << endl; cout << "------------" << endl; //操作b就相當於操作a本身 b = 100; cout << "a:" << a << endl; cout << "b:" << b << endl; cout << "------------" << endl; //一個變量可以有n個別名 int& c = a; c = 200; cout << "a:" << a << endl; cout << "b:" << b << endl; cout << "c:" << c << endl; cout << "------------" << endl; //a,b,c的地址都是相同的 cout << "a:" << &a << endl; cout << "b:" << &b << endl; cout << "c:" << &c << endl; } //2. 使用引用注意事項 void test02(){ //1) 引用必須初始化 //int& ref; //報錯:必須初始化引用 //2) 引用一旦初始化,不能改變引用 int a = 10; int b = 20; int& ref = a; ref = b; //不能改變引用 //3) 不能對數組建立引用 int arr[10]; //int& ref3[10] = arr; } //1. 建立數組引用方法一 typedef int ArrRef[10]; int arr[10]; ArrRef& aRef = arr; for (int i = 0; i < 10;i ++){ aRef[i] = i+1; } for (int i = 0; i < 10;i++){ cout << arr[i] << " "; } cout << endl; //2. 建立數組引用方法二 int(&f)[10] = arr; for (int i = 0; i < 10; i++){ f[i] = i+10; } for (int i = 0; i < 10; i++){ cout << arr[i] << " "; } cout << endl;
2. 函數中的引用
最常見看見引用的地方是在函數參數和返回值中。當引用被用作函數參數的時,在函數內對任何引用的修改,將對還函數外的參數產生改變。當然,可以通過傳遞一個指針來做相同的事情,但引用具有更清晰的語法。
如果從函數中返回一個引用,必須像從函數中返回一個指針一樣對待。當函數返回值時,引用關聯的內存一定要存在。
//值傳遞 void ValueSwap(int m,int n){ int temp = m; m = n; n = temp; } //地址傳遞 void PointerSwap(int* m,int* n){ int temp = *m; *m = *n; *n = temp; } //引用傳遞 void ReferenceSwap(int& m,int& n){ int temp = m; m = n; n = temp; } void test(){ int a = 10; int b = 20; //值傳遞 ValueSwap(a, b); cout << "a:" << a << " b:" << b << endl; //地址傳遞 PointerSwap(&a, &b); cout << "a:" << a << " b:" << b << endl; //引用傳遞 ReferenceSwap(a, b); cout << "a:" << a << " b:" << b << endl; }
通過引用參數產生的效果同按地址傳遞是一樣的。引用的語法更清楚簡單:
1) 函數調用時傳遞的實參不必加“&”符
2) 在被調函數中不必在參數前加“*”符
引用作為其它變量的別名而存在,因此在一些場合可以代替指針。C++主張用引用傳遞取代地址傳遞的方式,因為引用語法容易且不易出錯。
不能返回局部變量的引用。
函數當左值,必須返回引用。
//返回局部變量引用 int& TestFun01(){ int a = 10; //局部變量 return a; } //返回靜態變量引用 int& TestFunc02(){ static int a = 20; cout << "static int a : " << a << endl; return a; } int main(){ //不能返回局部變量的引用 int& ret01 = TestFun01(); //如果函數做左值,那么必須返回引用 TestFunc02(); TestFunc02() = 100; TestFunc02(); return EXIT_SUCCESS; }
3. 引用的本質
引用的本質在c++內部實現是一個指針常量.
Type& ref = val; // Type* const ref = &val; |
c++編譯器在編譯過程中使用常指針作為引用的內部實現,因此引用所占用的空間大小與指針相同,只是這個過程是編譯器內部實現,用戶不可見。
//發現是引用,轉換為 int* const ref = &a; void testFunc(int& ref){ ref = 100; // ref是引用,轉換為*ref = 100 } int main(){ int a = 10; int& aRef = a; //自動轉換為 int* const aRef = &a;這也能說明引用為什么必須初始化 aRef = 20; //內部發現aRef是引用,自動幫我們轉換為: *aRef = 20; cout << "a:" << a << endl; cout << "aRef:" << aRef << endl; testFunc(a); return EXIT_SUCCESS; }
4. 指針引用
在c語言中如果想改變一個指針的指向而不是它所指向的內容,函數聲明可能這樣:
void fun(int**); |
給指針變量取一個別名。
Type* pointer = NULL; Type*& = pointer; |
Type* pointer = NULL; Type*& = pointer;
struct Teacher{ int mAge; }; //指針間接修改teacher的年齡 void AllocateAndInitByPointer(Teacher** teacher){ *teacher = (Teacher*)malloc(sizeof(Teacher)); (*teacher)->mAge = 200; } //引用修改teacher年齡 void AllocateAndInitByReference(Teacher*& teacher){ teacher->mAge = 300; } void test(){ //創建Teacher Teacher* teacher = NULL; //指針間接賦值 AllocateAndInitByPointer(&teacher); cout << "AllocateAndInitByPointer:" << teacher->mAge << endl; //引用賦值,將teacher本身傳到ChangeAgeByReference函數中 AllocateAndInitByReference(teacher); cout << "AllocateAndInitByReference:" << teacher->mAge << endl; free(teacher); }
對於c++中的定義那個,語法清晰多了。函數參數變成指針的引用,用不着取得指針的地址。
5. 常量引用
常量引用的定義格式:
const Type& ref = val; |
常量引用注意:
1.字面量不能賦給引用,但是可以賦給const引用
2.const修飾的引用,不能修改。
void test01(){ int a = 100; const int& aRef = a; //此時aRef就是a //aRef = 200; 不能通過aRef的值 a = 100; //OK cout << "a:" << a << endl; cout << "aRef:" << aRef << endl; } void test02(){ //不能把一個字面量賦給引用 //int& ref = 100; //但是可以把一個字面量賦給常引用 const int& ref = 100; //int temp = 200; const int& ret = temp; }
[const引用使用場景]
常量引用主要用在函數的形參,尤其是類的拷貝/復制構造函數。
將函數的形參定義為常量引用的好處:
- 引用不產生新的變量,減少形參與實參傳遞時的開銷。
- 由於引用可能導致實參隨形參改變而改變,將其定義為常量引用可以消除這種副作用。
如果希望實參隨着形參的改變而改變,那么使用一般的引用,如果不希望實參隨着形參改變,那么使用常引用。
//const int& param防止函數中意外修改數據 void ShowVal(const int& param){ cout << "param:" << param << endl; }