C++Primer:函数(参数传递:引用形参)
1.問(wèn)題的引入
考慮下面不適宜復(fù)制實(shí)參的例子,該函數(shù)希望交換兩個(gè)實(shí)參的值:
<span style="font-size:18px;">void swap(int v1, int v2) {int temp = v2;v2 = v1;v1 = temp; } int main() {int i = 10;int j = 20;cout << "Before swap():\ti: "<< i << "\tj: " << j << endl;swap(i, j);cout << "After swap():\ti: "<< i << "\tj: " << j << endl;return 0; }</span>
編譯并執(zhí)行程序,產(chǎn)生如下輸出結(jié)果:
Before swap(): i: 10 ?j: 20
After swap(): ? ?i: 10 ?j: 20
這個(gè)例子期望改變實(shí)參本身的值。但是對(duì)于上述的函數(shù)定義,swap無(wú)法影響實(shí)參本身。執(zhí)行swap時(shí),只交換了實(shí)參的局部副本,而傳遞swap的實(shí)參并沒(méi)有修改。
為了使swap函數(shù)以期望的方式進(jìn)行工作,交換實(shí)參的值,需要將形參定義為引用類型:
<span style="font-size:18px;">// ok: swap acts on references to its arguments void swap(int &v1, int &v2) { int tmp = v2; v2 = v1; v1 = tmp; }</span>與所有的引用一樣,引用形參直接關(guān)聯(lián)所綁定的實(shí)參,他是實(shí)參的一個(gè)別名,而不是這些對(duì)象所謂的副本。定義引用時(shí),必須用與該引用綁定的對(duì)象初始化該引用。引用形參完全以相同的方式進(jìn)行工作。每次調(diào)用該函數(shù)時(shí),引用形參被創(chuàng)建并與相應(yīng)的實(shí)參關(guān)聯(lián)。此時(shí),調(diào)用該函數(shù)時(shí),swap(i,j);
形參v1只是對(duì)象i的另一個(gè)名字,而v2則是對(duì)象j的另外一個(gè)名字。對(duì)v1的任何修改實(shí)際上也是對(duì)i的修改。所以此時(shí)可以完成兩個(gè)值的交換任務(wù)。
從C語(yǔ)言背景轉(zhuǎn)到C++的我們習(xí)慣通過(guò)傳遞指針來(lái)實(shí)現(xiàn)對(duì)實(shí)參的訪問(wèn)。在C++中,使用引用形參更安全和更自然。
2.使用引用形參返回額外的信息
通過(guò)對(duì)上例的討論,可以理解如何利用引用形參讓函數(shù)修改實(shí)參的值。引用形參的另一種用法是向主調(diào)函數(shù)返回額外的結(jié)果。函數(shù)只能返回單個(gè)值,但有些時(shí)候,函數(shù)有不止一個(gè)的內(nèi)容需要返回。例如,定義一個(gè) find_val 函數(shù)。在一個(gè)整型 vector 對(duì)象的元素中搜索某個(gè)特定值。如果找到滿足要求的元素,則返回指向該元素的迭代器;否則返回一個(gè)迭代器,指向該 vector 對(duì)象的 end 操作返回的元素。此外,如果該值出現(xiàn)了不止一次,我們還希望函數(shù)可以返回其出現(xiàn)的次數(shù)。在這種情況下,返回的迭代器應(yīng)該指向具有要尋找的值的第一個(gè)元素。如何定義既返回一個(gè)迭代器又返回出現(xiàn)次數(shù)的函數(shù)?可以定義一種包含一個(gè)迭代器和一個(gè)計(jì)數(shù)器的新類型。而更簡(jiǎn)便的解決方案是給 find_val 傳遞一個(gè)額外的引用實(shí)參,用于返回出現(xiàn)次數(shù)的統(tǒng)計(jì)結(jié)果:
<span style="font-size:18px;">// returns an iterator that refers to the first occurrence of value // the reference parameter occurs containn a second return value vector<int>::const_iterator find_val(vector<int>::const_iterator beg, vector<int>::const_iterator end, <span style="color:#cc0000;"><strong> int value</strong></span>, // the value we want<strong><span style="color:#ff0000;">vector<int>::size_type &occurs</span></strong>) // number of times it occurs {// res_iter will hold first occurrence, if anyvector<int>::const_iterator res_iter = end;<span style="color:#ff0000;"><strong>occurs = 0;</strong></span> // set occurrence count parameter ///引用,即別名,我們可以在主函數(shù)體中查到for ( ; beg != end; ++beg)if (*beg == value) {// remember first occurrence of valueif (res_iter == end){ res_iter = beg;}++occurs; // increment occurrence count}return res_iter; // count returned implicitly in occurs }</span>
it = find_val(ivec.begin(), ivec.end(), 42, ctr);
調(diào)用 find_val 時(shí),需傳遞四個(gè)實(shí)參:一對(duì)標(biāo)志 vector 對(duì)象中要搜索的元素范圍的迭代器,所查找的值,以及用于存儲(chǔ)出現(xiàn)次數(shù)的size_type 類型對(duì)象。假設(shè) ivec 是 vector<int>類型的對(duì)象,it 是一個(gè)適當(dāng)類型的迭代器,而 ctr 則是 size_type 類型的變量。
調(diào)用后,ctr 的值將是 42 出現(xiàn)的次數(shù),如果 42 在 ivec 中出現(xiàn)了,則 it將指向其第一次出現(xiàn)的位置;否則,it 的值為 ivec.end(),而 ctr 則為 0。
3. 利用const引用避免復(fù)制(const int&)
在向函數(shù)傳遞大型對(duì)象時(shí),需要使用引用形參,這是引用形參適用的另一種情況。雖然復(fù)制實(shí)參對(duì)于內(nèi)置數(shù)據(jù)類型的對(duì)象或者規(guī)模較小的類類型對(duì)象來(lái)說(shuō)沒(méi)有什么問(wèn)題,但是對(duì)于大部分的類類型或者大型數(shù)組,它的效率太低了;此外,也會(huì)注意某些類類型是無(wú)法復(fù)制的。使用引用形參,函數(shù)可以直接訪問(wèn)實(shí)參對(duì)象,而無(wú)須復(fù)制它。
舉個(gè)例子: 我們需要編寫這樣一個(gè)功能:我們要比較兩個(gè)string對(duì)象長(zhǎng)度。這個(gè)函數(shù)需要訪問(wèn)每個(gè)string對(duì)象的大小,但是沒(méi)有必要修改這些對(duì)象。由于string對(duì)象可能相當(dāng)長(zhǎng),所以應(yīng)該避免采用復(fù)制操作。此時(shí),采用const 引用就非常合適。 <span style="font-size:18px;">// compare the length of two strings bool isShorter(<strong><span style="color:#ff0000;">const string&</span></strong> s1, <span style="color:#ff0000;"><strong>const string&</strong></span> s2) {return s1.size() < s2.size(); }</span>注意:如果使用引用形參的唯一目的是避免復(fù)制實(shí)參,則應(yīng)將形參定義為const引用。4.更靈活的指向const的引用
如果函數(shù)具有普通的非const引用形參,則顯然不能通過(guò)從const對(duì)象進(jìn)行調(diào)用。畢竟,此時(shí)函數(shù)可以修改傳遞進(jìn)來(lái)的對(duì)象,這樣就違背了實(shí)參的const特性。但比較容易忽略的是,調(diào)用這樣的函數(shù)(非const引用形參)時(shí),傳遞一個(gè)右值或具有需要轉(zhuǎn)換的對(duì)象同樣是不允許的。 <span style="font-size:18px;">// function takes a non-const reference parameter int incr(int &val) {return ++val; } int main() {short v1 = 0;const int v2 = 42;int v3 = incr(v1); // error: v1 is not an intv3 = incr(v2); // error: v2 is const<strong><span style="color:#ff0000;">v3 = incr(0); // error: literals are not lvalues</span></strong>v3 = incr(v1 + v2); // error: addition doesn't yield an lvalueint v4 = incr(v3); // ok: v3 is a non const object type int }</span>問(wèn)題的關(guān)鍵在于:非const引用形參只能與完全同類型的非const對(duì)象關(guān)聯(lián)!!! 舉一個(gè)例子: <span style="font-size:18px;">// returns index of first occurrence of c in s or s.size() if c isn't in s // Note: s doesn't change, so it should be a reference to const string::size_type find_char(<span style="color:#ff0000;"><strong>string &s</strong></span>, char c) {string::size_type i = 0;while (i != s.size() && s[i] != c)++i; // not found, look at next characterreturn i; }</span>if (find_char("Hello World", 'o')) // ...
雖然字符串字面值可以轉(zhuǎn)換為string對(duì)象,但是上述的調(diào)用仍然會(huì)導(dǎo)致編譯失敗。盡管函數(shù)并沒(méi)有修改這個(gè)形參的值,但是這樣的定義帶來(lái)的問(wèn)題是不能通過(guò)字符串字面值調(diào)用函數(shù)。
應(yīng)該說(shuō),更鼓勵(lì)將不加以修改的實(shí)參定義為const引用,因?yàn)閷⑿螀⒍x為非const引用,將毫不必要地限制了這些函數(shù)的使用(比如字面值字符串查找某一元素)!
注意:應(yīng)當(dāng)將不需要修改的引用形參定義為const引用。普通的非const引用形參在使用時(shí)非常不靈活。非const形參既不允許const對(duì)象初始化,也不能用字面值或產(chǎn)生右值的表達(dá)式實(shí)參初始化,大大地限制了函數(shù)功能。
5.傳遞指向指針的引用
假設(shè)想編寫一個(gè)與前面交換兩個(gè)整數(shù)的 swap 類似的函數(shù),實(shí)現(xiàn)兩個(gè)指針的交換。已知需用 * 定義指針,用 & 定義引用。現(xiàn)在,問(wèn)題在于如何將這兩個(gè)操作符結(jié)合起來(lái)以獲得指向指針的引用。這里給出一個(gè)例子:
<span style="font-size:18px;">// swap values of two pointers to int void ptrswap(int* &v1, int* &v2) {int *tmp = v2;v2 = v1;v1 = tmp; }</span>形參: int* &v1;
應(yīng)該從右至左理解:v1是一個(gè)引用(別名),與指向int型對(duì)象的指針相關(guān)聯(lián)。也就是說(shuō),v1只是傳遞金ptrswap函數(shù)的任意指針的別名。
<span style="font-size:18px;">int main() {int i = 10;int j = 20;int *pi = &i; // pi points to iint *pj = &j; // pj points to jcout << "Before ptrswap():\t*pi: "<< *pi << "\t*pj: " << *pj << endl;ptrswap(pi, pj); // now pi points to j; pj points to icout << "After ptrswap():\t*pi: "<< *pi << "\t*pj: " << *pj << endl;return 0; }</span>解釋:形參實(shí)參關(guān)系可以理解為 int* ?&v1 (pi); ?實(shí)際上,傳進(jìn)來(lái)了兩個(gè)指針,最終完成的是指針的交換!
即指針的值被交換了。在調(diào)用 ptrswap 時(shí),pi 指向 i,而 pj 則指向 j。在 ptrswap 函數(shù)中, 指針被交換, 使得調(diào)用 ptrswap 結(jié)束后, pi 指向了原來(lái) pj所指向的對(duì)象。換句話說(shuō),現(xiàn)在 pi 指向 j,而 pj 則指向了 i。
總結(jié)
以上是生活随笔為你收集整理的C++Primer:函数(参数传递:引用形参)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C++Primer:函数(参数传递-非引
- 下一篇: 飞鸽传书的设计应当具有“完整性”