一、預設情況下,函數的傳回值是按值傳遞的
這意味著得到控制權的函數將接收返回語句中指定的運算式的拷貝,例如:
Matrix grow( Matrix* p ) {
Matrix val;
// ...
return val;
}
grow()把儲存在 val 中的值的拷貝返回到調用函數,但調用函數不能用任何方式修改val 。
二、該預設行為可以被改變,一個函數可以被聲明為返回一個指標或一個引用
當函數返回一個引用時,調用函數接收 val 的左值,即調用函數可以修改 val 或取它的地址。grow()可以如下聲明返回一個引用:
Matrix& grow( Matrix* p ) {
Matrix *res;
// 在動態儲存裝置中分配一個更大的 Matrix
// res 是指向新 Matrix 的指標
// 將*p 內容複寫到*res
return *res;
}
如果傳回值是一個大型類對象,用引用或指標傳回型別比按值返回類對象效率要高得多,在某些情況下編譯器自動將按值返迴轉換到按引用返回,該最佳化被稱為命名傳回值最佳化( named return value optimization )。
當聲明一個返回引用的函數時,程式員應當知道下面兩個易犯的錯誤:
1. 返回一個指向局部對象的引用 局部對象的生命期隨函數的結束而結束,在函數結束後,該引用變成未定義記憶體的別名,例如:
// 問題: 返回一個指向局部對象的引用
Matrix& add( Matrix &m1, Matrix &m2 )
{
Matrix result;
if ( m1.isZero() )
return m2;
if ( m2.isZero() )
return m1;
// 將兩個 Matrix 對象的內容相加
// 喔! 返回之後 結果指向一個有問題的位置
return result;
}
在這種情況下,傳回型別應該被聲明為非參考型別,然後再在局部對象的生前拷貝局部變數
Matrix add( ...
2. 函數返回一個左值 對傳回值的任何修改都將改變被返回的實際對象,例:
#include <vector>
int &get_val( vector<int> &vi, int ix ) {
return vi[ix];
}
int ai[4] = { 0, 1, 2, 3 };
vector<int> vec( ai, ai+4 ); // 將 ai 的 4 個元素複製到 vec
int main() {
// 將 vec[0] 增加到 1
get_val( vec,0 )++;
// ...
}
為防止對引用傳回值的無意修改,傳回值應該被聲明為const :
const int &get_val( ...