簡介
(reference type)
“引用”(reference)是c++的一種新的變數類型,是對C的一個重要補充。它的作用是為變數起一個別名。假如有一個變數a,想給它起一個別名,可以這樣寫:
int a;int &b=a;
這就表明了b是a的“引用”,即a的別名。經過這樣的聲明,使用a或b的作用相同,都代表同一變數。在上述引用中,&是“引用聲明符”,並不代表地址。
不要理解為“把a的值賦給b的地址”。引用類型的數據存儲在記憶體的堆中,而記憶體單元中只存放堆中對象的地址。聲明引用並不開闢記憶體單元,b和a都代表同一變數單元。
注意:在聲明引用變數類型時,必須同時使之初始化,即聲明它代表哪一變數。在聲明一個變數的引用之後,在本函式執行期間,該引用一直與其代表的變數相聯繫
,不能再作為其他變數的別名。下面的用法不對:
int a1,a2;
int &b=a1;
int &b=a2;//企圖使b變成a2的別名(引用)是不行的。這樣是錯誤的。
我們可以把a2的值賦給b。
b=a2;
區別
引用和指針的區別
看實例吧:
引用是C++中的概念,初學者容易把引用和指針混淆一起。
下面的程式中,n是m的一個引用(reference),m是被引用物(referent)。
int m;
int &n = m;
n相當於m的別名(綽號),對n的任何操作就是對m的操作。
所以n既不是m的拷貝,也不是指向m的指針,其實n就是m它自己。
引用的規則
(1)引用被創建的同時必須被初始化(指針則可以在任何時候被初始化)。
(2)不能有NULL引用,引用必須與合法的存儲單元關聯(指針則可以是NULL)。
(3)一旦引用被初始化,就不能改變引用的關係(指針則可以隨時改變所指的對象)。
以下示例程式中,k被初始化為i的引用。
語句k = j並不能將k修改成為j的引用,只是把k的值改變成為6。
由於k是i的引用,所以i的值也變成了6。
int i = 5;
int j = 6;
int &k = i;
k = j; // k和i的值都變成了6;
主要功能
引用的主要功能:傳遞函式的參數和返回值。
C++語言中,函式的參數和返回值的傳遞方式有三種:值傳遞、指針傳遞和引用傳遞。
以下是"值傳遞"的示例程式。
由於Func1函式體內的x是外部變數n的一份拷貝,改變x的值不會影響n, 所以n的值仍然是0。
void Func1(int x)
{
x = x + 10;
}
...
int n = 0;
Func1(n);
cout << "n = " << n =" 0" n =" 0;" n = " << n << endl; // n = 10 以下是" size="14">
void Func3(int &x) { x = x + 10; } ... int n = 0; Func3(n); cout << "n = " << n =" 10">
(1) 在實際的程式中,引用主要被用做函式的形式參數--通常將類對象傳遞給一個函式.引用必須初始化. 但是用對象的地址初始化引用是錯誤的,我們可以定義一個指針引用。
1 int ival = 1092;
2 int &re = ival; //ok
3 int &re2 = &ival; //錯誤
4 int *pi = &ival;
5 int *π2 = pi; //ok
(2) 一旦引用已經定義,它就不能再指向其他的對象.這就是為什麼它要被初始化的原因。
(3) const引用可以用不同類型的對象初始化(只要能從一種類型轉換到另一種類型即可),也可以是不可定址的值,如文字常量。例如
double dval = 3.14159;
//下3行僅對const引用才是合法的
const int &ir = 1024;
const int &ir2 = dval;
const double &dr = dval + 1.0;
上面,同樣的初始化對於非const引用是不合法的,將導致編譯錯誤。原因有些微妙,需要適當做些解釋。
引用在內部存放的是一個對象的地址,它是該對象的別名。對於不可定址的值,如文字常量,以及不同類型的對象,編譯器為了實現引用,必須生成一個臨時對象,引用實際上指向該對象,但用戶不能訪問它。
例如:
double dval = 23;
const int &ri = dval;
編譯器將其轉換為:
int tmp = dval; // double -> int
const int &ri = tmp;
同理:上面代碼
double dval = 3.14159;
//下3行僅對const引用才是合法的
const int &ir = 1024;
const int &ir2 = dval;
const double &dr = dval + 1.0;
內部轉化為:
double dval = 3.14159;
//不可定址,文字常量
int tmp1 = 1024;
const int &ir = tmp1;
//不同類型
int tmp2 = dval;//double -> int
const int &ir2 = tmp2;
//另一種情況,不可定址
double tmp3 = dval + 1.0;
const double &dr = tmp3;
(4) 不允許非const引用指向需要臨時對象的對象或值,即,編譯器產生臨時變數的時候引用必須為const!!!!切記!!
int iv = 100;
int *πr = &iv;//錯誤,非const引用對需要臨時對象的引用
int *const πr = &iv;//ok
const int ival = 1024;
int *π_ref = &ival; //錯誤,非const引用是非法的
const int *π_ref = &ival; //錯誤,需要臨時變數,且引用的是指針,而pi_ref是一個非常量指針
const int * const π_ref = &ival; //正確
//補充
const int *p = &ival;
const int *π_ref = p; //正確
(5) ********對於const int *const & pi_ref = &iva; 具體的分析如下:*********
1.不允許非const引用指向需要臨時對象的對象或值
int a = 2;
int &ref1 = a;// OK.有過渡變數。
const int &ref2 = 2;// OK.編譯器產生臨時變數,需要const引用
2.地址值是不可定址的值
int * const &ref3 = &a; // OK;
3.於是,用const對象的地址來初始化一個指向指針的引用
const int b = 23;
const int *p = &b;
const int *& ref4 = p;
const int *const & ref5 = &b; //OK
const引用的語義到底是什麼?
最後,我們可能仍然不明白const引用的這個const的語義是什麼
const引用表示,試圖通過此引用去(間接)改變其引用的對象的值時,編譯器會報錯!
這並意味著,此引用所引用的對象也因此變成const類型了。我們仍然可以改變其指向對象的值,只是不通過引用
下面是一個簡單的例子:
1 #include<iostream>
2 using namespace std;
3
4 int main()
5 {
6 int val = 1024;
7 const int &ir = val;
8
9 val++;
10 //ir++;
11
12cout<<"val="<<val<<"\n";
13cout<<"ir="<<ir;
14return0;
15 }
其中第10行,如果我們通過ir來改變val的值,編譯時會出錯。但是我們仍然可以通過val直接改變其值(第9行)
總結:const引用只是表明,保證不會通過此引用間接的改變被引用的對象!
另外,const既可以放到類型前又可以放到類型後面,放類型後比較容易理解:
string const *t1;
const string *t1;
typedef string* pstring;string s;
const pstring cstr1 = &s;就出錯了
但是放在類型後面不會出錯:
pstring const cstr2 = &s;
使用
引用在類中的使用
1. #include<iostream>
2. using namespace std;
3.
4. class A
5. {
6. public:
7. A(int i=3):m_i(i){}
8. void print()
9. {
10. cout<<"m_i="<<m_i<<endl;
11. }
12. private:
13. int m_i;
14. };
15.
16. class B
17. {
18. public:
19. B(){}
20. B(A& a):m_a(a){}
21. void display()
22. {
23. m_a.print();
24. }
25. private:
26. A& m_a;
27. };
28.
29.
30. int main(int argc,char** argv)
31. {
32. A a(5);
33. B b(a);
34. b.display();
35. return0;
36. }
注意
引用在類中使用需注意
其中,要注意的地方就是引用類型的成員變數的初始化問題,它不能直接在構造函數裡初始化,必須用到初始化列表,且形參也必須是引用類型。
凡是有引用類型的成員變數的類,不能有預設構造函式。原因是引用類型的成員變數必須在類構造時進行初始化。
如果兩個類要對第三個類的數據進行共享處理,可以考慮把第三個類作為這兩個類的引用類型的成員變數