1. 程式人生 > >常量引用和變數引用的區別理解

常量引用和變數引用的區別理解

C++primer提到,引用就是物件的別名,換言之,計算機在對物件引用的時候並沒有實際分配空間,這個可以從引用物件的角度去測試:

假設有:

class A

{

   ......

};

A a;

A b=a;//呼叫拷貝建構函式,也叫型別轉換

A &c=a;//給a取一個別名,不呼叫任何函式

那麼既然引用只是名稱,那麼sizeof(type&)是什麼呢,我的猜想是,sizeof()並不在乎裡面的名稱是什麼,他只關心型別,對於引用,那麼就是結果就是,

引用什麼型別,那麼返回什麼型別的大小,這個用reinterpret_cast關鍵字進行測試:

A a;//假設a的大小是24,這個根據A類大小設定
int &b=reinterpret_cast<int&> (a);
cout<<"物件a的大小是:"<<sizeof (a)<<endl;
cout<<"引用b的大小是:"<<sizeof (b)<<endl;
cout<<"他們的地址分別是"<<endl<<(int*)(&a)<<endl<<(int*)&b;

可以看出,他們的地址都是一樣的,也就是說,是同一個物件,但是sizeof只會看int&是int型的引用,就直接返回int的大小4個位元組了。

2.引用的是變數名的情況

假設有:

double b=3.33;

int &a=(int)b和int &a=(int&)b;有什麼區別

對於前者,是型別轉換,對於型別轉換,計算機實際是通過一定方式,生成一個臨時的,且符合轉換型別的變數,(int)b就好比 (A)a,我們知道,a就是A類的物件,

(A)a有意義嗎,實際是有的,他會呼叫拷貝建構函式生成一個臨時的物件,如果拷貝建構函式有顯示的輸出語句,那麼就一目瞭然了。再進行引申,如果

A是一個類名,可以用 A b=A(a);來呼叫拷貝建構函式,這個與A b=a;有什麼區別呢,區別就在於,對於A b=A(a);那麼先通過A (a)進行型別轉化,這時候用a

構造一個臨時物件,再執行A b=臨時物件,但是這個語句不會再次呼叫拷貝建構函式,這個原因說起來就要提起c++ primer第13章關於合成的複製建構函式的定義了,

合成建構函式的行為是,執行逐個成員初始化。也就是說,在程式設計師沒有自己定義建構函式的時候,是執行合成的建構函式的,但是,還有一種情況,即使自定義

了拷貝建構函式,也會執行合成的建構函式,那就是,當等號的右邊是一個臨時物件的時候將不會執行已定義的拷貝建構函式,而是執行預設的合成複製建構函式。

所以對於 A b=(A)a來說, A(a)生成的是臨時物件,所以最後那步A b=臨時物件呼叫的是預設的複製建構函式,吧並不會呼叫使用者設定的拷貝建構函式。所以最終

自定的拷貝建構函式只在(A)a時被呼叫,而如果使用者自定義的拷貝建構函式是 A (A&),那麼對於有些編譯器是會對上述行為報錯的,原因很簡單,因為當他要執行

A b=臨時物件時,發現等號右邊是一個右值,引用是不能對右值引用的(A&=臨時物件),那麼變異會報錯,這時只需把自定義的拷貝建構函式改為A (const A&)就行了,

因為C++是允許右值引用的,例如const int &a=3;那麼由(A)a生成的臨時變數就能作為形參被傳遞了。但是對於 A b=a;就不同了,這個是直接把a作為形參呼叫

拷貝建構函式,所以即使使用者定義的拷貝建構函式是A (A&)也就是不允許接受右值作為形參也不影響,因為a是實際佔用空間的。那回過頭,對於前面的

double b=3.33

int &a=(int)b;#1實際是申請一個臨時空間,把b按照int的規格寫入這個空間

int &a=(int&)b;#2則不同了,他直接把b的儲存內容當成int型別,也就是裡面一堆的1010101的二進位制,沒有任何實際的轉化

這兩個的區別就是,最後當你cout<<a的時候,#2是會輸出異常的,因為他沒經過規格化的型別轉換,只是把機器碼的意思改變了,由double變成int了,

這個問題在c就有了,例如

double d=5.55;

int *p=(int*) &d;

printf ("%lf",*p);//輸出異常,因為*p裡面的機器碼被解釋為int

printf ("%lf",*(double*)p);//輸出正常,強扭後,*p被解釋為double

可見。同樣對機器碼,不用的解釋會影響最後的輸出。

但是對於int &a=(int)b又會有新問題,(int)b生成的是臨時變數,這個是不能付給引用的,不出意外會報錯,只需把int&a改為const int&a即可。

3.常量引用

對於const int& a=3.33;

因為常量是不能取地址的,(&3.33非法),所以根本不可能給他取別名,實際的做法分為兩步

1)(int)3.33生成一個臨時空間

2)把這個空間與引用關聯

類似const int&a=(int)3.33;所以當你多次對一個常量進行引用,返回的只是不同的臨時空間地址而已

例如:

const int& p1=3.33;

const int&p2=3.33;

const int&p3=3.33;

p1,p2,p3的地址都是不同的。