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;
}
可以看到通過傳值并沒有成功進行排序,這是因為當我們将 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 )
其中主函數中調用函數文法不需要改變,且結果為預期結果。
其中還有一點值得注意,就是使用 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 所代表的對象一經聲明則不會改變。