函數的值傳遞和引用傳遞

  函數在每次調用時都會重新創建它的形參,並用傳入的實參進行初始化。形參初始化的機理與變量初始化一樣。
  和其他變量一樣,形參的類型決定了形參和實參交互的方式。如果形參是引用類型,它將綁定到對應的實參上;否則,將實參的值拷貝後賦給形參。
  當形參是引用類型時,我們說它對應的實參被引用傳遞(pass by reference)或者函數被傳引用調用(called by reference)。和其他引用一樣,引用形參也是它綁定的對象的別名;也就是說,引用形參是它對應的實參的別名。
  當實參的值被拷貝給形參時,形參和實參是兩個相互獨立的對象。我們說這樣的實參被值傳遞(passed by value)或者函數被傳值調用(called by value)。

1.傳值參數
  當初始化一個非引用類型的變量時,初始值被拷貝給變量。此時,對變量的改動不會影響初始值:

int n = 0;    //int類型的初始變量
int i = n;    //i是n的值的副本
i = 42;       //i的值改變;n的值不變

  傳值參數的機理完全一樣,函數對形參做的所有操作都不會影響實參。舉個例子,我們準備編寫一個求數的階乘的程序:

int fact(int val) {
    int ret = i;
    while(val > 1) {
    ret *= val--;
    return ret;
}

  儘管fact函數改變了val的值,但是這個改動不會影響傳入fact的實參。調用fact(i)不會改變i的值。

2.指針形參
  指針形參的行爲和其他非引用類型一樣。當執行指針拷貝操作時,拷貝的是指針的值。拷貝之後,兩個指針是不同的指針。因爲指針使我們可以間接地訪問它所指的對象,所以通過指針可以修改它所知對象的值:

int n = 0, i = 42;
int *p = &n, *q = &i;
*p = 42;
p = q;

  指針形參的行爲與之類似。舉個例子,我們準備編寫一個函數,使用指針形參交換兩個整數的值。

void swap(int *p,int *q){
    int temp = *p;
    *p = *q;
    *q = temp;
} 

  如圖所示,虛線的矩形框內表示傳函數的形參,當傳入兩個int*類型的地址時,對形參的改變是不會影響到實參的。
這裏寫圖片描述

  測試代碼如下:

#include<iostream>
using namespace std;
void swap(int *p,int *q){
    int temp = *p;
    *p = *q;
    *q = temp;
} 

int main() {
    int a=3,b=14;
    cout<<"Before swap:"<<"a="<<a<<",b="<<b<<endl;
    cout<<"Location:"<<"a="<<&a<<",b="<<&b<<endl;
    swap(&a,&b);
    cout<<"After swap:"<<"a="<<a<<",b="<<b<<endl;
    cout<<"Location:"<<"a="<<&a<<",b="<<&b<<endl;
    return 0;
}

這裏寫圖片描述

  我們發現,調用swap函數之後,兩個實參&a和&b所指的對象被交換了,但是實參本身沒有改變。
  熟悉C的程序員常常使用指針類型的形參訪問函數外部對象。在C++語言中,建議使用引用類型的形參代替指針。

3.傳引用參數
  我們知道對於引用的操作實際上是作用在引用所指的對象上。引用形參的行爲與之類似,通過使用引用形參,允許函數改變一個或多個實參的值。
  舉個例子:

void reset(int &i) {
    i = 0;
}

  和其他引用一樣,引用形參綁定初始化它的對象。當調用reset函數時,i綁定我們傳給函數的Int對象,此時改變i的值也就是改變i所引對象的值。此例中,被改變的對象是傳入reset的實參。
  調用reset函數時,我們直接傳入對象而無須傳入對象的地址:

int j = 42;
reset(j);
cout<<"j = "<<j<<endl; 

  在上述過程中,形參i僅僅是j的又一個名字。在reset內部對i的使用即對j的使用。
  拷貝大的類類型對象或者容器對象效率比較低,甚至有的類類型(包括IO類型在內)根本就不支持拷貝操作。當某種類型不支持拷貝操作時,函數只能通過引用形參訪問該類型的對象。
  舉個例子,我們編寫一個函數比較兩個string對象的長度。因爲string對象可能會非常長,所以應該儘量避免直接拷貝他們,這是使用引用形參是比較明智的選擇。有因爲比較長度無須改變string對象的內容,所以把形參定義成對常量的引用。

bool isShorter(const string &s1, const string &s2) {
    return s1.size() < s2.size();
}

  如果函數無需改變引用形參的值,最好將其聲明爲常量引用。

3.參考資料
《C++ Primer》6.2 參數傳遞

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章