2008年7月23日 星期三

c++ - 什麼是「參照」- reference

在C++中,副程式參數的傳遞分為三種:「call by value」、「call by address」、「call by reference」

而C++語言中可以對變數取另一個別名。這一個功能稱為參照(reference),例如使用參照對變數num取newnum這一樣一個別名,就會如下所示

int num;
int &newnum = num;


製作別名時,不管是原變數名還是別名,都可以做相同的存取。只要變更任何一方的值,另一方也會跟著改變。

newnum = 13;
cout << "num = " << num << endl;
cout << "newnum = " << newnum << endl;

注意最後一行,存取newnum時,不需要&

在C語言中使用指標,也可以得到同樣的效果。但指標在進行存取時,必需要對所指的資料加上*,因此比較複雜。

int num;
int *newnum = &num;
*newnum = 400;


而參照使用的地方最主要是「對函數的引數使用」
像下列一樣宣告引數時,使用參照可以對函數傳值

double Addnum(double &a, double &b)
{
  return a + b;
}


但是,這樣其實跟平常的call by value感覺好像差不多,以下就建立一個我覺得最有用的地方。


void caculate(double a, double b, double &sum, double &sub)
{
  sum = a + b;
  sub = a - b;
}

而我們這樣使用此函數

double first = 6;
double second = 4;
double ans1,ans2;
caculate(first,second,ans1,ans2);
printf("ans1 is %lg\nans2 is %lg\n",ans1,ans2);

注意到沒?
在之前若我們要由副程式傳回兩個結果,我們通常必需在副程式中建立一個陣列,
並且傳回那一個陣列的指標,
但是,若透過參照的話,就簡單很多了。

其實,參照就像「肥仔魚」這一個變數,我們另外把它的名字取作「阿肥」、「阿仔」、「阿魚」,其實,這是個都是同一個東西,我們對其中一個做改變,那麼當讀取任何一個值時,都會是改變過時候的值。
有一點類似指標啦,只不過在使用的時候更加便利,不需要加*哩

這裡另外提到把參照加上const的形式

int x;
const int &y = x;
x = 100; // ok
y = 200; // error

由x去改值沒有問題,但若由y去改值會出現錯誤


這裡有一個程式

...
struct Person
{
  char name[50];
  int age;
};

void PrintPersonRef(Person &psn); // 傳參照
void PrintPersonPtr(Person *psn); // 傳址
void PrintPersonVal(Person psn); // 傳值

int main()
{
  PrintPersonRef(shain1); // 不加&
  PrintPersonPtr(&shain1); // 加&
  PrintPersonVal(shain1); // 不加&
...
}

看到這裡就會浮現一個問題
因為傳參照與傳值函數的呼叫方法相同,會導致不知道要不要變更引數的值
為了避免這一個問題,在函數中要變更引數的值時,使用「傳址」比較好
若沒有要變更引數的話,利用const 加上參照速度會比傳值還要快
這樣比較方便以後程式的維護

整理如下:
不變更引數+小型資料=傳值
不變更引數+結構體等大資料=執行const後的傳參照值
變更引數+小型資料=傳址
變更引數+結構體等大資料=傳址


[2008.9.5]補充
回傳值為reference
例:
const QWMatrix & QCanvasView::inverseWorldMatrix () const

參考資料:博碩文化-新C++學習繪本

沒有留言: