天天看點

[Essential C++] 傳值(pass by value)與傳址(pass by reference)Pass by value/reference/pointer

Pass by value/reference/pointer

在定義或調用一個函數時,用兩種參數傳遞方式,傳值(pass by value)與傳址(pass by reference)。使用一個冒泡排序的程式作為例子進行了解。

傳值(pass by value)

傳值是将傳給函數的對象複制一份,原對象和複制品之間沒有任何關聯。

當我們調用一個函數時,會在記憶體中建立起一塊特殊的區域,稱為“程式棧(progam stack)”。這塊特殊區域提供了每個函數參數的存儲空間,它也提供函數所定義的每個對象的記憶體空間——這些對象叫做local object(局部對象)。一旦函數完成,這塊記憶體就會被釋放掉。如下為傳值的冒泡排序:

#include <iostream>
#include <vector>

using namespace std;

void display( vector<int> vec )//在終端上顯示向量
{
    for ( int i = 0; i < vec.size(); i++ )
    {
        cout << vec[i] << ' ';
    }
    cout << endl;
}

void swap( int val1, int val2 )//交換兩個對象,此時為傳值。
{
    int temp = val1;
    val1 = val2;
    val2 = temp;
}

void bubble_sort( vector<int> vec )//冒泡排序算法
{
    for ( int i = 0; i < vec.size(); i++ )
    {
        for( int j = i+1; j < vec.size(); j++ )
        {
            if ( vec[i] > vec[j] )
            {
                swap( vec[i], vec[j] );
            }
        }
    }
}

int main()
{
    int ia[8]={ 8, 34, 3, 13, 1, 21, 5, 2 };
    vector<int> vec( ia, ia+8 );
    cout << "vector before sort: ";
    display( vec );
    bubble_sort( vec );
    cout << "vector after sort: ";
    display( vec );
    return 0;
}
           
[Essential C++] 傳值(pass by value)與傳址(pass by reference)Pass by value/reference/pointer

可以看到通過傳值并沒有成功進行排序,這是因為當我們将 vec[i] 這樣的對象傳入函數時,預設情形下會被複制一份,成為參數的局部性定義(local definition)。我們在 bubble_sort() 内傳給 swap() 的對象,和我們在 swap() 内操作的對象,其實是沒有任何關聯的兩組對象。

傳址(pass by reference)

傳值是将傳給函數的對象位址複制一份,函數中對該對象的任何操作,都相當于是對傳入的對象進行間接操作

referecne 扮演着外界與對象之間的一個間接号碼牌的角色,隻要在型别名稱和 reference 名稱之間插入 & 符号,便是聲明了一個 reference:

int ival = 1024; 
int *pi = &ival; //指針,指向一個 int 對象,這裡的 &為取址
int &rval = ival; // refernce,這裡的 &為引用,代表一個int對象
rval = 4096;//對 reference 進行的操作會反映在本體上
pi = &rval;//将 iravl 的位址賦給pi
           

面對 reference 的所有操作都像面對 “reference 所代表的對象” 所進行的操作沒有差別,當我們以 reference 作為函數參數時,亦是如此。在這裡我們将 swap() 以及bubble_sort() 的傳參方式改為傳址:

void swap( int &val1, int &val2 )
void bubble_sort( vector<int> &vec )
           

其中主函數中調用函數文法不需要改變,且結果為預期結果。

[Essential C++] 傳值(pass by value)與傳址(pass by reference)Pass by value/reference/pointer

其中還有一點值得注意,就是使用 reference 傳參時因為不複制對象隻複制位址,可以降低複制大型對象的負擔,例如将打算顯示的 vector 以傳址方式傳入 display() 中,速度會更快:

我們聲明了一個 reference to const vector, 因為函數之中并不會更改 vector 的内容。加上 const 可以讓閱讀程式的人了解,我們以傳址的方式來傳遞 vector,為的是避免指派操作,而不是為了要在函數之中對它進行修改。

pointer 與 reference 的差別

我們也可以将參數以 pointer 形式傳遞。這和以 reference 傳遞的效果相同,傳遞的是對象位址,而不是整個對象的複制品。

使用 pointer 與使用 reference 的方法不同:

void display( const vector<int> *vec )
{
    if ( ! vec ) //需判斷 pointer 是否為空
    {
        cout << "display(): the vector pointer is 0\n";
        return;
    }
    for ( int i = 0; i < vec->size(); i++)//此處 vec 為 pointer,是以不能使用'.',需要使用'->'來調用成員函數
    {
        cout << (*vec)[i] << '';
    }
    cout << endl;
}

int main()
{
    int ia[8]={ 8, 34, 3, 13, 1, 21, 5, 2 };
    vector<int> vec( ia, ia+8 );
    cout << "vector before sort: ";
    display( &vec );//傳入vec的位址
    bubble_sort( vec );
    cout << "vector after sort: ";
    display( vec );
    return 0;
}
           
  • pointer 可以指向空,當我們 dereference pointer 時,一定要确定其值并非0;而 reference 不能指向空,必定會代表某個對象,不需檢查。然而以下情況需要避免,會造成錯誤。
int *p = null;
int& r = *p;
           
  • pointer 所指的對象可以改變,而 reference 所代表的對象一經聲明則不會改變。

繼續閱讀