天天看點

函數傳回值是否使用引用類型的問題:了解引用、傳回值

  這樣做的好處在于:(1)保證在運算中,c 與 d 的值不會意外被 修改;(2)d 對應的實際參數可以為常量也可以為變量。

  在本文中,主要讨論函數(尤其是與類相關的函數——成員函數或友元函數)傳回值類型何時用引用的問題。

  一、函數傳回值為引用的典型案例

  在做輸入輸出重載時,重載函數傳回流對象,如:

  在例程1的第11和12行,運算符重載函數的傳回值類類被聲明為引用;在第22行和30行,這兩個函數的實作中,分别傳回了輸入流對象input和輸出流對象output(要注意到這兩個對象是作為形式參數出現的,且都為引用)。實際上,<<和>>運算符對其他類型重載時也是這樣處理的。這樣處理的好處在于,函數傳回的輸入/出流還可以繼續用于其他資料的輸入/出,使我們能用cin>>i,j;和cout<<i<<","<<j<<endl;的形式輸入/出。

  以例程1中的第69行(cout<<"c1="<<c1<<endl; )為例。

  cout<<"c1="<<c1<<endl; 中首先執行的是oprate<<(cout,"c1=")。實際參數——輸出流對象cout(本身為引用)傳遞給形式參數output,在完成輸出字元串"c1="的任務後,cout這個引用對象作為傳回值傳回到被調用處,于是餘下的未執行的部分相當于:cout<<c1<<endl;。這是例程1中定義的重載發揮作用的時候了,輸出c1後,cout被傳回,再次用于輸出換行符endl。

  這裡值得總結的是:(1)傳回值為引用時,傳回的變量仍然要繼續完成相關的工作;(2)傳回的引用值本身也必須是引用,一般是在調用函數中存在的,以引用型形式參數的方式傳遞到函數中的變量(例程1中的input和output為引用)。

  二、一個令人驚訝的程式:給函數的傳回值指派

  這個例子來自《c++ primer(第四版)》。

  正如注釋中所講,傳回值為引用,函數調用get_val(s,0)居然可以作為指派表達式的左值,就這樣,其引用的空間(s[0])中所存儲的字元被指派為‘a’,“引用是被傳回元素的同義詞”,此處完全等同于s[0]='a'。

  程式從文法上講沒有問題,運作結果也達到了舉例的目的。本例僅在于展示這種用法,了解引用作為函數傳回值。在工程中,這種風格的程式當然不推薦使用,當不希望引用傳回值被修改時,将傳回值聲明為const,即:

  const char &get_val(string &str, string::size_type ix)

  三、加法運算的重載結果也定義為引用,如何?

  對于例程1,将其中的加法重載函數的傳回值也定義為引用(當然,這樣做純屬撞錯),結果會怎樣?先給出這樣的程式,請注意在第14、15、44和52行中增加的&。

  這個程式運作結果可能與例程1的結果是一樣的。的确,在我運作中,結果沒有出現過異常。這個程式在vs2008下編譯時會有兩個警告:

  1>d:\c++\vs2008 project\example\example\example.cpp(49) : warning c4172: 傳回局部變量或臨時變量的位址

  1>d:\c++\vs2008 project\example\example\example.cpp(57) : warning c4172: 傳回局部變量或臨時變量的位址

  這兩個警告道出了危險所在:第49行和57行傳回臨時變量c之後,c 的空間将被釋放,也就意味着可以由系統進行再配置設定,作其他用途使用了。而傳回值類型為引用時,傳回的值仍然在使用着這一塊記憶體區域。結果隻能是“可能”正确,毫無保障。好比付款買了房(對方收條都沒有開),房産證卻放在房管局大廳中,哪一天你被趕出家門,那是活該的。這個簡單的例子沒有出問題純屬意外,因為操作簡單,那片空間還沒有被重新配置設定。

  但是,最值得警惕的還是那些出問題可能性更小的時候,“不以惡小而為之”,一貫正常,隻有很小幾率出的錯的情況更可怕。

  最重要的,了解了上述道理,要做到:千萬不要傳回對局部變量的引用。

  還有一條類似的:千萬不要傳回局部變量的指針。

  四、鑽個牛角尖:operate+就要傳回引用

  重溫本文第一部分最後的加粗字:(2)傳回的引用值本身也必須是引用,一般是在調用函數中存在的,以引用型形式參數的方式傳遞到函數中的變量(例程1中的input和output為引用)。要應付這種胡攪蠻纏式的要求,我們也隻能圍繞這個要求想辦法。

  給出的一種解決辦法是:

  這種實作中,c1在調用operate+()前已經存在,是作為引用進行參數傳遞的。這樣的變量能夠保證程式不會出現意外。但是,會出的代價是,c1的值在參與加法運算時被改變了。在第40行執行了c3=c1+c2後,第41行顯示的c1的值同c3的值相同,是相加後的結果。

  這種安排也隻能接受這種結局。事實上,學習計算機的同學也要接受這種風格,在有些語言(例如,彙編以及被冠以高雅稱号的函數式語言)中,運算就是這麼完成的。c1+c2怎麼完成?add c1, c2; 其結果如何取出?結果就儲存到第一個運算量中。

  牛角尖再鑽深些,不能這樣做!我隻能為傳回引用再使一招了:提前定義儲存結果的變量(例c),并将之作為參數傳遞到函數中。付出的代價是,加運算的運算量成了3個,operate+()形式是不能用了(運算符重載不能改變其目數)。實際上,傳回的那個引用也沒有什麼意思了,結果已經由引用 c 帶回來了,傳回值甚至可以為void。這種設計太差了,程式依然貼在下面,讀者不看也罷。

  

<本文完>

繼續閱讀