轉自:http://wenku.baidu.com/view/3e1957270066f5335a8121cc.html
http://see.xidian.edu.cn/cpp/biancheng/view/50.html
一、引用作為函數參數
作為函數參數時引用有兩種原因:
在函數內部會對此參數進行修改
提高函數調用和運行效率
關於第一點,都知道C++里提到函數就會提到形參和實參。如果函數的參數實質就是形參,不過這個形參的作用域只是在函數體內部,
也就是說實參和形參是兩個不同的東西,要想形參代替實參,肯定有一個值的傳遞。函數調用時,值的傳遞機制是通過“形參=實參”
來對形參賦值達到傳值目的,產生了一個實參的副本。即使函數內部有對參數的修改,也只是針對形參,也就是那個副本,實參不會
有任何更改。函數一旦結束,形參生命也宣告終結,做出的修改一樣沒對任何變量產生影響。
例如:
void swap(int p1, int p2) //對兩個變量進行交換處理。此處函數的形參為p1, p2,沒有引用 {int p; p=p1; p1=p2; p2=p; }
void main( ) {int a,b; cin>>a>>b; //輸入a,b兩變量的值 swap(a,b); //直接以變量a和b作為實參調用swap函數 cout<<a<< ' ' <<b; //輸出結果
你會發現輸出的a和b還是你輸入的值,沒有交換。
如果我們改為:
void swap(int &p1, int &p2) //對兩個變量進行交換處理。此處函數的形參為p1, p2都是引用 { int p; p=p1; p1=p2; p2=p; }
再次執行,就會發現值交換了。
原理就在於采用&p1和&p2時,p1和p2是實參的別名而已,像一個指針指向實參。改變p1和p2就是改變實參的值。
關於第二點,可以結合第一點分析,p1和p2是實參的引用,不用經過值的傳遞機制,已經有了實參值的信息。所以沒有了傳值和生成副本的時間和空間消耗。當程序對效率要求比較高時,這是非常必要的,比如單片機。所以函數參數時的引用又被形象地稱為:“虛實結合”。
二、引用作為函數返回值
這一部分就引用我收集的資料
說明:
(1)以引用返回函數值,定義函數時需要在函數名前加&
(2)用引用返回一個函數值的最大好處是,在內存中不產生被返回值的副本。
例如:
#include <iostream.h> float temp; //定義全局變量temp float fn1(float r); //聲明函數fn1 float &fn2(float r); //聲明函數fn2 float fn1(float r) //定義函數fn1,它以返回值的方法返回函數值 { temp=(float)(r*r*3.14); return temp; } float &fn2(float r) //定義函數fn2,它以引用方式返回函數值 { temp=(float)(r*r*3.14); return temp; }
void main() //主函數 { float a=fn1(10.0); //第1種情況,系統生成要返回值的副本(即臨時變量) float &b=fn1(10.0); //第2種情況,可能會出錯(不同 C++系統有不同規定) //不能從被調函數中返回一個臨時變量或局部變量的引用 float c=fn2(10.0); //第3種情況,系統不生成返回值的副本 //可以從被調函數中返回一個全局變量的引用 float &d=fn2(10.0); //第4種情況,系統不生成返回值的副本 //可以從被調函數中返回一個全局變量的引用 cout<<a<<c<<d; }
引用作為返回值,必須遵守以下規則:
(1)不能返回局部變量的引用。主要原因是局部變量會在函數返回后被銷毀,因此被返回的引用就成為了"無所指"的引用,程序會進入未知狀態。
(2)不能返回函數內部new分配的內存的引用。雖然不存在局部變量的被動銷毀問題,可對於這種情況(返回函數內部new分配內存的引用),又面臨其它尷尬局面。例如,被函數返回的引用只是作為一 個臨時變量出現,而沒有被賦予一個實際的變量,那么這個引用所指向的空間(由new分配)就無法釋放,造成memory leak。
(3)可以返回類成員的引用,但最好是const。主要原因是當對象的屬性是與某種業務規則(business rule)相關聯的時候,其賦值常常與某些其它屬性或者對象的狀態有關,因此有必要將賦值操作封裝在一個業務規則當中。如果其它對象可以獲得該屬性的非常 量引用(或指針),那么對該屬性的單純賦值就會破壞業務規則的完整性。
引用和指針一樣,是C++里面的一個大話題。關於引用的問題還有很多,比如const引用、引用與一些操作符的重載、引用和多態、引用與拷貝構造函數"X(&X)"等等。需要各位自己動手過程發現和解決了。因為和本文主題相關不大,所以就不贅述了。
指針在函數中的使用也是十分廣泛的。某些情況下,將指針作為函數的參數或函數的返回值會給我們帶來方便。而某些情況下,我們又不得不將指針作為函數的參數或函數的返回值。
指針作為參數
我們在上一章我們已經了解向函數傳遞數組的實質是向函數傳遞數組首元素的地址。我們又知道數組名是一個指向數組首元素的指針常量。所以我們認為,向函數傳遞數組是將指針作為參數的特殊形式。
由於指針可以直接操作內存中的數據,所以它可以用來修改實參。這個功能和引用是類似的。
下面我們來看一段程序,了解指針作為參數時的上述兩個特點:(程序8.6.1)
#include "iostream.h" void arrayCopy(int *src,int *dest,int size);//復制數組元素 void display(const int *array,int size);//輸出數組元素 int main() { int a[]={3,4,5,6,3,1,6}; int b[7]; arrayCopy(a,b,sizeof(a)/sizeof(int));//把數組a的元素依次復制到數組b中 cout <<"The data of array a is:"; display(a,sizeof(a)/sizeof(int)); cout <<"The data of array b is:"; display(b,sizeof(b)/sizeof(int)); return 0; } void arrayCopy(int *src,int *dest,int size) { for (int i=0;i<size;i++) { dest[i]=src[i];//修改了實參數組元素 } cout <<size <<" data Copied." <<endl; } void display(const int *array,int size)//const用來保護指針指向的數據 { for (int i=0;i<size;i++) { cout <<array[i] <<" "; } cout <<endl; } 運行結果: 7 data Copied. The data of array a is:3 4 5 6 3 1 6 The data of array b is:3 4 5 6 3 1 6
根據arrayCopy函數,不難看出傳遞數組和傳遞指針是完全相同的。而通過指針的間接引用或數組操作,我們可以在函數內實現對實參的修改。這就是arrayCopy函數能夠實現復制功能的原因。
不過,將指針作為函數參數的副作用仍然不容我們忽視。指針和引用雖然都能夠修改實參,但是指針卻更加危險。因為引用僅限於修改某一個確定的實參,而指針卻可以指向內存中的任何一個數據,通過間接引用就能夠在一個函數內修改函數外甚至系統中的數據了。這樣一來,函數的黑盒特性就被破壞了,系統也因此變得不再安全。對於程序員來說,將指針作為函數參數可能把函數內的問題引到函數外面去,使得調試程序變得非常困難。所以,我們要認識到使用指針的兩面性,謹慎對待指針做函數參數。
為了避免指針作為函數參數導致數據被意外修改,我們可以使用const來保護指針指向的數據,如程序8.6.1中的display函數。
指針作為返回值
和別的數據類型一樣,指針也能夠作為函數的一種返回值類型。我們把返回指針的函數稱為指針函數。在某些情況下,函數返回指針可以給我們設計程序帶來方便。而且此時通過間接引用,函數的返回值還可以作為左值。
下面我們來看一段程序,了解函數如何返回指針:(程序8.6.2)
#include "iostream.h" int * max(int *array,int size);//返回值類型是int *,即整型指針 int main() { int array[]={5,3,6,7,2,1,9,10}; cout <<"The Max Number is " <<*max(array,sizeof(array)/sizeof(int)) <<endl;//間接引用返回的指針 return 0; } int * max(int *array,int size)//尋找最大值 { int *max=array; for (int i=0;i<size;i++) { if (array[i]>*max) max=&array[i];//記錄最大值的地址 } return max; } 運行結果: The Max Number is 10
需要注意的是,返回的指針所指向的數據不能夠是函數內聲明的變量。道理很簡單,我們在第六章已經說明,一個函數一旦運行結束,在函數內聲明的變量就會消失。就好像下課同學們都走了,教室里的某一個座位到底有沒有坐着誰我們無法確定。所以指針函數必須返回一個函數結束運行后仍然有效的地址值。
