值傳遞:ios
形參是實參的拷貝,改變形參的值並不會影響外部實參的值。從被調用函數的角度來講,值傳遞是單向的(實參->形參),參數的值只能傳入,c++
不能傳出。當函數內部須要修改參數,而且不但願這個改變影響調用者時,採用值傳遞。api
指針傳遞:安全
形參爲指向實參地址的指針,當對形參的指向操做時,就至關於對實參自己進行的操做函數
引用傳遞:spa
形參至關因而實參的「別名」,對形參的操做其實就是對實參的操做,在引用傳遞過程當中,被調函數的形式參數雖然也做爲局部變量在棧3d
中開闢了內存空間,可是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操做都被處理成間接尋址,即經過指針
棧中存放的地址訪問主調函數中的實參變量。正由於如此,被調函數對形參作的任何操做都影響了主調函數中的實參變量。code
舉例:對象
#include<iostream> using namespace std; //值傳遞 void change1(int n){ cout<<"值傳遞--函數操做地址"<<&n<<endl; //顯示的是拷貝的地址而不是源地址 n++; } //引用傳遞 void change2(int & n){ cout<<"引用傳遞--函數操做地址"<<&n<<endl; n++; } //指針傳遞 void change3(int *n){ cout<<"指針傳遞--函數操做地址 "<<n<<endl; *n=*n+1; } int main(){ int n=10; cout<<"實參的地址"<<&n<<endl; change1(n); cout<<"after change1() n="<<n<<endl; change2(n); cout<<"after change2() n="<<n<<endl; change3(&n); cout<<"after change3() n="<<n<<endl; return true; }
運行結果:
能夠看出,實參的地址爲0x28ff2c
採用值傳遞的時候,函數操做的地址是0x28ff10並非實參自己,因此對它進行操做並不能改變實參的值
再看引用傳遞,操做地址就是實參地址 ,只是至關於實參的一個別名,對它的操做就是對實參的操做
接下來是指針傳遞,也可發現操做地址是實參地址
那麼,引用傳遞和指針傳遞有什麼區別嗎?
引用的規則:
(1)引用被建立的同時必須被初始化(指針則能夠在任什麼時候候被初始化)。
(2)不能有NULL引用,引用必須與合法的存儲單元關聯(指針則能夠是NULL)。
(3)一旦引用被初始化,就不能改變引用的關係(指針則能夠隨時改變所指的對象)。
指針傳遞的實質:
指針傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞過程當中,被調函數的形式參數做爲被調函數的局部變量處理,
即在棧中開闢了內存空間以存放由主調函數放進來的實參的值,從而成爲了實參的一個副本。值傳遞的特色是被調函數對形式參數的
任何操做都是做爲局部變量進行,不會影響主調函數的實參變量的值。(這裏是在說實參指針自己的地址值不會變)
指針傳遞和引用傳遞通常適用於:
函數內部修改參數而且但願改動影響調用者。對比指針/引用傳遞能夠將改變由形參「傳給」實參(實際上就是直接在實參的內存上修改,
不像值傳遞將實參的值拷貝到另外的內存地址中才修改)。
另一種用法是:當一個函數實際須要返回多個值,而只能顯式返回一個值時,能夠將另外須要返回的變量以指針/引用傳遞
給函數,這樣在函數內部修改而且返回後,調用者能夠拿到被修改事後的變量,也至關於一個隱式的返回值傳遞吧。
如下原文出處地址:http://xinklabi.iteye.com/blog/653643
從概念上講。指針從本質上講就是存放變量地址的一個變量,在邏輯上是獨立的,它能夠被改變,包括其所指向的地址的改變和其指向的地址中所存放的數據的改變。
而引用是一個別名,它在邏輯上不是獨立的,它的存在具備依附性,因此引用必須在一開始就被初始化,並且其引用的對象在其整個生命週期中是不能被改變的(自始至終只能依附於同一個變量)。
在C++中,指針和引用常常用於函數的參數傳遞,然而,指針傳遞參數和引用傳遞參數是有本質上的不一樣的:
指針傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞過程當中,被調函數的形式參數做爲被調函數的局部變量處理,即在棧中開闢了內存空間以存放由主調函數放進來的實參的值,從而成爲了實參的一個副本。值傳遞的特色是被調函數對形式參數的任何操做都是做爲局部變量進行,不會影響主調函數的實參變量的值。(這裏是在說實參指針自己的地址值不會變)
而在引用傳遞過程當中,被調函數的形式參數雖然也做爲局部變量在棧中開闢了內存空間,可是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操做都被處理成間接尋址,即經過棧中存放的地址訪問主調函數中的實參變量。正由於如此,被調函數對形參作的任何操做都影響了主調函數中的實參變量。
引用傳遞和指針傳遞是不一樣的,雖然它們都是在被調函數棧空間上的一個局部變量,可是任何對於引用參數的處理都會經過一個間接尋址的方式操做到主調函數中的相關變量。而對於指針傳遞的參數,若是改變被調函數中的指針地址,它將影響不到主調函數的相關變量。若是想經過指針參數傳遞來改變主調函數中的相關變量,那就得使用指向指針的指針,或者指針引用。
爲了進一步加深你們對指針和引用的區別,下面我從編譯的角度來闡述它們之間的區別:
程序在編譯時分別將指針和引用添加到符號表上,符號表上記錄的是變量名及變量所對應地址。指針變量在符號表上對應的地址值爲指針變量的地址值,而引用在符號表上對應的地址值爲引用對象的地址值。符號表生成後就不會再改,所以指針能夠改變其指向的對象(指針變量中的值能夠改),而引用對象則不能修改。
最後,總結一下指針和引用的相同點和不一樣點:
★相同點:
●都是地址的概念;
指針指向一塊內存,它的內容是所指內存的地址;而引用則是某塊內存的別名。
★不一樣點:
●指針是一個實體,而引用僅是個別名;
●引用只能在定義時被初始化一次,以後不可變;指針可變;引用「從一而終」,指針能夠「見異思遷」;
●引用沒有const,指針有const,const的指針不可變;(具體指沒有int& const a這種形式,而const int& a是有 的, 前者指引用自己即別名不能夠改變,這是固然的,因此不須要這種形式,後者指引用所指的值不能夠改變)
●引用不能爲空,指針能夠爲空;
●「sizeof 引用」獲得的是所指向的變量(對象)的大小,而「sizeof 指針」獲得的是指針自己的大小;
●指針和引用的自增(++)運算意義不同;
●引用是類型安全的,而指針不是 (引用比指針多了類型檢查)
1、引用的概念
引用引入了對象的一個同義詞。定義引用的表示方法與定義指針類似,只是用&代替了*。
例如: Point pt1(10,10);
Point &pt2=pt1; 定義了pt2爲pt1的引用。經過這樣的定義,pt1和pt2表示同一對象。
須要特別強調的是引用並不產生對象的副本,僅僅是對象的同義詞。所以,當下面的語句執行後:
pt1.offset(2,2);
pt1和pt2都具備(12,12)的值。
引用必須在定義時立刻被初始化,由於它必須是某個東西的同義詞。你不能先定義一個引用後才
初始化它。例以下面語句是非法的:
Point &pt3;
pt3=pt1;
那麼既然引用只是某個東西的同義詞,它有什麼用途呢?
下面討論引用的兩個主要用途:做爲函數參數以及從函數中返回左值。
2、引用參數
一、傳遞可變參數
傳統的c中,函數在調用時參數是經過值來傳遞的,這就是說函數的參數不具有返回值的能力。
因此在傳統的c中,若是須要函數的參數具備返回值的能力,每每是經過指針來實現的。好比,實現
兩整數變量值交換的c程序以下:
void swapint(int *a,int *b)
{
int temp;
temp=*a;
a=*b;
*b=temp;
}
使用引用機制後,以上程序的c++版本爲:
void swapint(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}
調用該函數的c++方法爲:swapint(x,y); c++自動把x,y的地址做爲參數傳遞給swapint函數。
二、給函數傳遞大型對象
當大型對象被傳遞給函數時,使用引用參數可以使參數傳遞效率獲得提升,由於引用並不產生對象的
副本,也就是參數傳遞時,對象無須複製。下面的例子定義了一個有限整數集合的類:
const maxCard=100;
Class Set
{
int elems[maxCard]; // 集和中的元素,maxCard 表示集合中元素個數的最大值。
int card; // 集合中元素的個數。
public:
Set () {card=0;} //構造函數
friend Set operator * (Set ,Set ) ; //重載運算符號*,用於計算集合的交集 用對象做爲傳值參數
// friend Set operator * (Set & ,Set & ) 重載運算符號*,用於計算集合的交集 用對象的引用做爲傳值參數
...
}
先考慮集合交集的實現
Set operator *( Set Set1,Set Set2)
{
Set res;
for(int i=0;i<Set1.card;++i)
for(int j=0;j>Set2.card;++j)
if(Set1.elems[i]==Set2.elems[j])
{
res.elems[res.card++]=Set1.elems[i];
break;
}
return res;
}
因爲重載運算符不能對指針單獨操做,咱們必須把運算數聲明爲 Set 類型而不是 Set * 。
每次使用*作交集運算時,整個集合都被複制,這樣效率很低。咱們能夠用引用來避免這種狀況。
Set operator *( Set &Set1,Set &Set2)
{ Set res;
for(int i=0;i<Set1.card;++i)
for(int j=0;j>Set2.card;++j)
if(Set1.elems[i]==Set2.elems[j])
{
res.elems[res.card++]=Set1.elems[i];
break;
}
return res;
}
3、引用返回值
若是一個函數返回了引用,那麼該函數的調用也能夠被賦值。這裏有一函數,它擁有兩個引用參數並返回一個雙精度數的引用:double &max(double &d1,double &d2){return d1>d2?d1:d2;}因爲max()函數返回一個對雙精度數的引用,那麼咱們就能夠用max() 來對其中較大的雙精度數加1:max(x,y)+=1.0;