Lab 3-0: Reference

 

 
實習目標 1. 瞭解什麼是 C++ 中的參考 (Reference)
2. 使用 C++ 中參考 (Reference) 的語法
 
說明一

什麼是參考(Reference) ?

在前面的實習裡,其實大家已經使用過 C++ 中參考的語法了,例如:

	int x;
	cin >> x;
或是
	string s;
	getline(cin, s);

上面這兩個範例中都是呼叫某一個函式,由標準輸入串流 (鍵盤) 讀取資料到記憶體的變數中,第一個範例其實 C++ 編譯器會轉換為

	cin.operator>>(x);

的成員函式呼叫。

在上面的例子中並沒有看到像我們使用 scanf() 函式時頭痛的取位址運算符號

	int x;
	scanf("%d", &x);

以前使用 scanf() 時, 好不容易習慣了要加上 & 符號來讓函式直接存取呼叫端的變數, 但是 operator>>(x) 函式不用這種語法還是會把資料讀入變數 x 中, 這不是有一點神奇了嗎?

相信第一次學到 scanf() 這個函式時,許多同學都會覺得這個 & "取位址運算符號" 的意義很令人頭痛、很模糊,就算你比較熟悉 C 語言裡指標的語法與意義之後,不可諱言地,這個 & 符號還是很容易漏掉、或是不小心混淆了,這有點像是 C 語法裡頭的一個陷阱

你也許會問:當寫程式的人寫出

    scanf("%d", x);

的程式時,沒有加上 & 取位址運算符號,程式難道有可能會正確嗎?有可能有意義嗎?不幸地,答案是肯定的。就是有這種用法,例如:

    void myFun(int *iaddr)
    {
        scanf("%d", iaddr);
    }
    ...
    void main()
    {
        int x;
        myFun(&x);
    }

所以 C 語言的設計者不可能讓編譯器自己偵測這種狀況,自己猜測程式設計者的意圖。(如果你知道 scanf() 這個函式的原型是 int scanf(const char *fmt, ...) 的話,你更加知道為了寫程式的彈性,C 的語法在這裡連參數的型態都不幫你檢查,如果你用錯了,完全不會有任何的提醒!)

不管怎樣,上面這種用法終究是比較少看到的,C++ 中參考的語法,可以讓函式傳回資料的語法變得比較簡單,比較不會出錯。

說明二

參考可以看成是 "不用 &* 運算子的指標變數"

請看下面 C 程式中指標變數運用的範例

    #include <cstdio>

    void twice(int *xaddr)
    {
        *xaddr = *xaddr * 2;
    }
    ...
    void main()
    {
        int x = 10;
        twice(&x);
        printf("twice of 10 is %d\n", x);
    }

在 C++ 中如果運用參考變數語法的話,要達到相同功能的程式如下:

    #include <cstdio>

    void twice(int &y)
    {
        y = y * 2;
    }
    void main()
    {
        int x = 10;
        twice(x);
        printf("twice of 10 is %d\n", x);
    }

此時變數 y就是 C++ 中所謂的參考變數, y 可以看成程式呼叫端變數 x 的另一個名稱, 在程式執行時 x 和 y 是完全一樣的一個記憶體變數。

比較一下這兩個程式不同的地方有 4 個,後三者都比原來使用指標的程式簡化一些。

請注意此為語法的修改,實際上兩組程式是等效的,第二組程式中 twice(x) 在執行時仍然需取得 x 變數的位址,以便在函式中直接存取該位址之記憶體內容。

請仔細瞭解上面這兩個程式,也許順便執行一下,確定一下。

說明三

參考變數是 "另一個變數的別名"

有的時候相同的變數在不同的程式段落中用不同的名字可以使得程式的意義較為清楚

    int x;
    int &y = x;
在做了這樣子的宣告之後, 不論你在程式中用 x 或是用 y 的效果是一樣的, 例如:
   y = 10; // 相當於 x = 10;
   scanf("%d", &y); // 相當於 scanf("%d", &x);

再看一個比較複雜的範例:

    struct myRecord
    {
        int x;
        char buf[100];
    } data1[200];

    struct myRecord (&data2)[200] = data1;

在這個範例中我們先定義了一個結構陣列 data1, 然後再定義它的別名 data2, 定義完了以後 data1 和 data2 是指同一個東西, 同樣的陣列變數但是有兩個名字。

請注意在定義 data2 時前後的小括號, 語法中並不允許如下的 reference array

    struct myRecord &data2[200] = data1;

我們以後上課的時候會再解釋一下有沒有小括號的差別。

請簡單測試一下上面的程式, 確定你完全了解。

步驟四 有如下 的 C++ 程式,其中函式參數 p1 及 p2 使用指標變數來完成,請將其修改為參考變數, 在函式裡 p1 與 p2 的使用也必須相對地修改,請編譯並執行
    #include <cstdio>

    void modifyParameters(int *p1, double *p2)
    {
        *p1 = *p1 * *p1;
        printf("請輸入參數二: ");
        scanf("%lf", p2);
    }

    void main()
    {
        int p1 = 25;
        double p2 = -1.3;
        modifyParameters(&p1, &p2);
        printf("修改過後的參數一 = %d\n", p1);
        printf("修改過後的參數二 = %lf\n", p2);
    }
步驟五 請助教檢查後, 將所完成的 project (去掉 debug/ 資料匣下的所有內容) 壓縮起來, 選擇 Lab3-0 上傳, 後面的實習課程可能需要使用這裡所完成的程式

C++ 物件導向程式設計課程 首頁

製作日期: 03/07/2005 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw TEL: 02 24622192x6615
海洋大學 電機資訊學院 資訊工程系 Lagoon