一、引用
引用是別名
必須在定義引用時進行初始化。初始化是指明引用指向哪個對象的唯一方法。
const 引用是指向 const 對象的引用:
const int ival = 1024; const int &refVal = ival; // ok: both reference and object are const int &ref2 = ival; // error: non const reference to a const object
可以讀取但不能修改 refVal ,因此,任何對 refVal 的賦值都是不合法的。這個限制有其意義:不能直接對 ival 賦值,因此不能通過使用 refVal 來修改ival。
同理,用 ival 初始化 ref2 也是不合法的:ref2 是普通的非 const 引用,因此可以用來修改 ref2 指向的對象的值。
通過 ref2 對 ival 賦值會導致修改const 對象的值。
為阻止這樣的修改,需要規定將普通的引用綁定到 const 對象是不合法的。
const 引用則可以綁定到不同但相關的類型的對象或綁定到右值。
int i = 42; // legal for const references only const int &r = 42; const int &r2 = r + i;
i是int類型,r是const int&類型
非 const 引用只能綁定到與該引用同類型的對象。
如果非 const 引用綁定到與該引用不同類型的對象:
double dval = 3.14; const int &ri = dval;
編譯器會把這些代碼轉換成如以下形式的編碼:
int temp = dval; // create temporary int from the double const int &ri = temp; // bind ri to that temporary
如果 ri 不是 const,那么可以給 ri 賦一新值。這樣做不會修改 dval,
而是修改了 temp。
二、return
任何返回類型不是 void 的函數必須返回一個值,而且這個返回值的類型必須和函數的返回類型相同,或者能隱式轉化為函數的返回類型。也就是說重要的是定義函數時返回值類型不是return的。
1、返回非引用類型
函數的返回值用於初始化在調用函數處創建的臨時對象。在求解表達式時,如果需要一個地方儲存其運算結果,編譯器會創建一個沒有命名的對象,這就是臨時對象。
int max (int a,int b) { return a>b?a:b; } int ret=max(1,8); //該語句在編譯器內部: temp =max(1,8); ret=temp;
temp就是函數返回的創建的臨時對象,該對象對ret進行初始化
用函數返回值初始化臨時對象與用實參初始化形參的方法是一樣的。如果返回類型不是引用,在調用函數的地方會將函數返回值復制給臨時對象。當函數返回非引用類型時,其返回值既可以是局部對象,也可以是求解表達式的結果。
例如,下面的程序提供了一個計數器、一個單詞 word 和單詞結束字符串ending,當計數器的值大於 1 時,返回該單詞的復數版本:
// return plural version of word if ctr isn't 1 string make_plural(size_t ctr, const string &word, const string &ending) { return (ctr == 1) ? word : word + ending; }
我們可以使用這樣的函數來輸出單詞的單數或復數形式。這個函數要么返回其形參 word 的副本,要么返回一個未命名的臨時string 對象,這個臨時對象是由字符串 word 和 ending 的相加而產生的。這兩種情況下,return 都在調用該函數的地方復制了返回的 string 對象。
2、返回的引用
當函數返回引用類型時,沒有復制返回值。相反,返回的是對象本身。
例如,考慮下面的函數,此函數返回兩個 string 類型形參中較短的那個字符串的引用:
const string &shorterString(const string &s1, const string &s2)
{
return s1.size() < s2.size() ? s1 : s2;
}
形參和返回類型都是指向 const string 對象的引用,調用函數和返回結果時,都沒有復制這些 string 對象。
千萬不要返回局部對象的引用
千萬不要返回指向局部對象的指針
當函數執行完畢時,將釋放分配給局部對象的存儲空間。此時,對局部對象的引用就會指向不確定的內存。考慮下面的程序:
// Disaster: Function returns a reference to a local object const string &manip(const string& s) { string ret = s; // transform ret in some way return ret; // Wrong: Returning reference to a local object! }
這個函數會在運行時出錯,因為它返回了局部對象的引用。當函數執行完畢,字符串 ret 占用的儲存空間被釋放,函數返回值指向了對於這個程序來說不再有效的內存空間。
函數的返回類型可以是大多數類型。特別地,函數也可以返回指針類型。和返回局部對象的引用一樣,返回指向局部對象的指針也是錯誤的。
一旦函數結束,局部對象被釋放,返回的指針就變成了指向不再存在的對象的懸垂指針。
補充:
懸垂指針指向曾經存放對象的內存,指針指向的對象已經不再存在了,但該指針仍存在。懸垂指針往往導致程序錯誤,而且很難檢測出來。
一旦刪除了指針所指向的對象,立即將指針置為 0,這樣就非常清楚地表明指針不再指向任何對象了。
3、引用返回左值
char &get_val(string &str, string::size_type ix)
{
return str[ix];
}
int main()
{
string s("a value");
cout << s << endl; // prints a value
get_val(s, 0) = 'A'; // changes s[0] to A
cout << s << endl; // prints A value
return 0;
}
給函數返回值賦值可能讓人驚訝,由於函數返回的是一個引用,因此這是正確的,該引用是被返回元素的同義詞。
如果不希望引用返回值被修改,返回值應該聲明為 const:
const char &get_val(...
C++Primer4th第四版中文版
