1. 引用
1.1 左值引用
我們通常提到的引用都是指“左值引用”, 其語(yǔ)法如下:
<類型名> & <變量名> = <變量名>;
左值引用就是alias,相當(dāng)于給對(duì)象起了個(gè)別名,當(dāng)定義引用時(shí),編譯器會(huì)把引用型變量與它的初始值(也是個(gè)變量名)綁定在一起,使用引用型變量跟使用其初始值變量等同,同時(shí)改變。
一旦完成綁定(初始化),引用型變量會(huì)和它的初始值一直綁定在一起,因此不能將引用再重新綁定到另一個(gè)變量身上。
對(duì)于重新綁定的問(wèn)題,來(lái)做個(gè)小實(shí)驗(yàn):
int main(int argc, char** argv){
int i = 10;
int ii = 12;
int &a = i;
a = ii;
cout << "a:" << a << endl
<< "i:" << i << endl
<< "ii:" << ii << endl;
return 0;
}
編譯后運(yùn)行,得到如下結(jié)果:
【結(jié)論】:
由以上實(shí)驗(yàn)結(jié)果可以看到,對(duì)于已經(jīng)綁定了i的引用型變量a,對(duì)其再賦值變量ii,并沒(méi)有報(bào)語(yǔ)法錯(cuò)誤。但是,其實(shí)際的操作結(jié)果,并不像我們的本來(lái)意圖(想讓a再與ii綁定在一起),而是對(duì)本來(lái)綁定在一起的a和i變量做了一次賦值,把ii的值傳遞給了a和i兩個(gè)變量。
因此,以后在代碼中使用引用型變量時(shí),切記,一旦綁定關(guān)系成立,不要試圖解除綁定關(guān)系。結(jié)果很可能會(huì)事與愿違。
1.2 右值引用(C++11)
我們通常所說(shuō)的引用都是“左值引用”。還有一種形式的引用叫做“右值引用”,是對(duì)數(shù)值的引用。其語(yǔ)法如下:
<類型名> && <變量名> = <右值>;
其特點(diǎn)是:只能綁定到一個(gè)將要銷毀的對(duì)象上,其作用就是把右值資源“移動(dòng)”到另一個(gè)對(duì)象中去。
這里暫不探討。
2. 參數(shù)傳遞
參數(shù)傳遞發(fā)生在函數(shù)調(diào)用階段,函數(shù)定義中參數(shù)列表里的參數(shù)叫做形式參數(shù),實(shí)際調(diào)用函數(shù)時(shí)傳入的參數(shù)叫做實(shí)際參數(shù)。
1.1 值傳遞(pass by value)
一般在參數(shù)傳遞的過(guò)程中,沒(méi)有特別標(biāo)注(不實(shí)用指針和引用),都是值傳遞,即在函數(shù)中發(fā)生的參數(shù)值的改變都是作用于形式參數(shù),而對(duì)實(shí)際參數(shù)沒(méi)有影響。
下面寫段代碼來(lái)觀察下值傳遞:
class varTest{
public:
static void increase(int var){
var++;
cout << "var:" << var << endl;
}
};
int main(int argc, char** argv){
int i = 10;
varTest::increase(i);
cout << "i:" << i << endl;
return 0;
}
程序編譯后運(yùn)行,得到如下結(jié)果:
1.2 引用傳遞(pass by reference)
有了上面"引用綁定"和"形參實(shí)參關(guān)系"分析的基礎(chǔ),那么,函數(shù)參數(shù)以引用方式傳遞就很好理解了,就是把形參和實(shí)參綁定在一起,如果在函數(shù)體里對(duì)實(shí)參有修改,修改會(huì)真的起作用。
還是一段代碼,驗(yàn)證下:
class varTest{
public:
static void increase(int &var){
var++;
cout << "var:" << var << endl;
}
};
int main(int argc, char** argv){
int i = 10;
varTest::increase(i);
cout << "i:" << i << endl;
return 0;
}
編譯運(yùn)行結(jié)果:
可以看到形參var和實(shí)參i的值都改變了。
1.3 常量型引用傳遞(pass by reference to const)
引用在編譯器底層的實(shí)現(xiàn)本質(zhì)上就是一個(gè)指針,或者說(shuō)跟指針的原理相同,這使它既能提高傳遞效率,同時(shí)看起來(lái)又好像是一個(gè)對(duì)象,使用起來(lái)更方便直觀。
所以現(xiàn)在一般都會(huì)建議程序員,在使用函數(shù)參數(shù)傳遞時(shí),盡量使用引用的方式傳遞,其開(kāi)銷相當(dāng)于一個(gè)4byte的指針類型的參數(shù)傳遞的開(kāi)銷。可以緩解函數(shù)調(diào)用時(shí)壓棧帶來(lái)的負(fù)擔(dān)。
但有些情況下,雖然傳遞引用型參數(shù)調(diào)用函數(shù)的效率會(huì)比較高,可是調(diào)用者也許并不希望函數(shù)內(nèi)部改變引用參數(shù)的數(shù)值,這就用到了傳遞常量型引用參數(shù)。
3.返回值
2.1 臨時(shí)變量與值傳遞返回值(return by value)
臨時(shí)變量通常指在一定生存周期(scope)范圍內(nèi)的變量,其在此范圍內(nèi)分配內(nèi)存(通常是棧上的內(nèi)存),跨出生命周期,變量的內(nèi)存將被釋放,變量消失。
前面說(shuō),盡量使用引用的方式傳遞參數(shù),同樣的道理也適用函數(shù)返回值的傳遞。但并不是所有情況下都可以用引用的方式返回值。當(dāng)返回值涉及到傳遞臨時(shí)變量就必須使用值傳遞的方式。
看代碼:
inline complex operator+(const complex& c1, const complex& c2){
return complex(c1.re+c2.re, c1.im+c2.im);
}
2.2 引用傳遞返回值(return by reference)
直接看代碼:
inline complex operator+=(complex *ths, const complex & c){
ths->re += c.re;
ths->im += c.im;
return *ths;
}
2.3 返回值傳遞到常量引用(return by reference to const)
返回值傳遞到常量引用跟返回值以引用方式傳遞一樣,其實(shí),函數(shù)開(kāi)發(fā)者,不需要關(guān)注引用型返回值是不是傳遞給了常量,傳遞給常量引用是使用者的考量。其編碼方式與上面的引用傳遞返回值是一樣的。
只不過(guò)要考率的地方是在調(diào)用時(shí),是否要使用常量引用型變量對(duì)返回值綁定的決策上。
【技巧】:參數(shù)和返回值傳遞的過(guò)程中,如果可以盡量使用引用的方式傳遞。