發新話題

C++ Gossip - 函式《進階議題》return 的傳值、傳參考

C++ Gossip - 函式《進階議題》return 的傳值、傳參考

在定義函式時,一定要定義函式的傳回值型態,如果函式不傳回值,則使用void表示不傳回任何數值;一旦指定函式的傳回值不為void,則在函式中一定要使用return傳回一個數值,否則編譯器將回報錯誤。

在之前的範例中,您只是使用傳值的方式傳回函式的執行結果,事實上您也可以傳回一個指標或是參考,傳回指標通常意味著您要對這個指標所指向的記憶體位置作取值或更動的動作,例如下面的程式中,您在函式中動態建立一個陣列,並傳回它的指標值:


#include <iostream>
using namespace std;

int* createArray(int);
void deleteArray(int*);

int main() {
    int m = 0;

    cout << "陣列大小: ";
    cin >> m;

    int *arr = createArray(m);

    for(int i = 0; i < m; i++) {
        arr = i;
    }
   
    for(int i = 0; i < m; i++) {
        cout << "arr[" << i << "] = "
             << arr << endl;
    }

    deleteArray(arr);

    return 0;
}

// 傳回建立的陣列位址
int* createArray(int m) {
    int *a = new int[m];

    for(int i = 0; i < m; i++) {
        a = 0;
    }

    return a;
}

void deleteArray(int* arr) {
     delete [] arr;
}
執行結果:
陣列大小: 5
arr[0] = 0
arr[1] = 1
arr[2] = 2
arr[3] = 3
arr[4] = 4

由於您使用動態配置的方式,所以在使用delete之前,這塊被配置的記憶體並不會自動清除,所以您可以直接傳回給呼叫函式的主函式,注意如果您不是使用 new來配置,則在副函式中所宣告的變數記憶體,在函式執行結束後都會自動消失,則您傳回指標值也就沒有意義,也會造成存取錯誤,因為該塊記憶體在副函式執行完畢後已經自動回收了。

在這邊的範例您也看到了如何傳遞陣列給函式,以及如何傳回一個陣列,在C++中傳遞陣列或傳回陣列一律使用傳址方法,因為陣列名稱本身就是個指標,儲存有位址資訊。

在進一步討論傳參考之前,您先要瞭解到,在C++中傳遞一個物件,預設也是使用傳值的方式,例如下面這個程式:


#include <iostream>
#include <string>
using namespace std;

string foo();

int main() {
    string str; // 空字串

    str = foo();

    cout << "address: " << &str
         << endl << str << endl;
   
    return 0;
}

string foo() {
    string s = "This is caterpillar speaking.";

    cout << "address: " << &s
         << endl << s << endl;

    return s;
}

執行結果:
address: 0x22ff40
This is caterpillar speaking.
address: 0x22ff50
This is caterpillar speaking.

您可以發現到,兩個字串的記憶體位置並不相同,表示傳回的字串在傳值時是複製一份新的傳回,您可以改用傳參考的方式將字串值傳回,方式如下所示:

#include <iostream>
#include <string>
using namespace std;

string& foo();

int main() {
    string &str = foo();

    cout << "address: " << &str
         << endl << str << endl;

    delete &str;

    return 0;
}

string& foo() {
    string *s = new string("This is caterpillar speaking.");

    cout << "address: " << s
         << endl << *s << endl;

    return *s;
}
執行結果:
address: 0x3d2458
This is caterpillar speaking.
address: 0x3d2458
This is caterpillar speaking.

使用傳參考的方式是直接告知物件的記憶體位置,而沒有作物件複製的動作,可以加快程式執行的效率,而某些情況下,編譯器也會自行將物件的傳回結果自動設定為傳參考,即使語法上並沒有指定,例如:

#include <iostream>
#include <string>
using namespace std;

string foo();

int main() {
    string str = foo(); // 空字串

    cout << "address: " << &str
         << endl << str << endl;
   
    str = foo(); // 指定新字串,會複製一份
   
    cout << "address: " << &str
         << endl << str << endl;
   
    return 0;
}

string foo() {
    string s = "This is caterpillar speaking.";

    cout << "address: " << &s
         << endl << s << endl;

    return s;
}
執行結果:
address: 0x22ff50
This is caterpillar speaking.
address: 0x22ff50
This is caterpillar speaking.
address: 0x22ff40
This is caterpillar speaking.
address: 0x22ff50
This is caterpillar speaking.

在這個程式中,第一行宣告str並指定foo()傳回值時,雖然並沒有指定傳回參考,但是您可以看到兩個字串變數的值都指向同一塊記憶體,但是編譯器會自動判斷並以傳參考的方式來傳回這個物件,而不是使用傳值的方法傳回,這是編譯器的功能,您可以將之當作一個效率上的考量作法,但不可以將之當作一個傳參考的方式來使用。

必須注意的是,函式中的區域變數在函式開始時被配置,在函式結束後所佔有的記憶體位址也會被清除,絕對不要傳回一個區域變數的位址給呼叫者,或是以傳參考的方式傳回一個區域變數,因為您所存取的記憶體位址中資料是未知的,所以結果是不可預期的。

TOP

發新話題

本站所有圖文均屬網友發表,僅代表作者的觀點與本站無關,如有侵權請通知版主會盡快刪除。