1. 程式人生 > >C/C++ 引用作為函式的返回值

C/C++ 引用作為函式的返回值

語法:型別 &函式名(形參列表){ 函式體 }

特別注意:

1.引用作為函式的返回值時,必須在定義函式時在函式名前將&

2.用引用作函式的返回值的最大的好處是在記憶體中不產生返回值的副本

//程式碼來源:RUNOOB
#include<iostream>
using namespace std;
float temp;
float fn1(float r){
    temp=r*r*3.14;
    return temp;
} 
float &fn2(float r){ //&說明返回的是temp的引用,換句話說就是返回temp本身
    temp=r*r*3.14;
    return temp;
}
int main(){
    float a=fn1(5.0); //case 1:返回值
    //float &b=fn1(5.0); //case 2:用函式的返回值作為引用的初始化值 [Error] invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
                           //(有些編譯器可以成功編譯該語句,但會給出一個warning) 
    float c=fn2(5.0);//case 3:返回引用
    float &d=fn2(5.0);//case 4:用函式返回的引用作為新引用的初始化值
    cout<<a<<endl;//78.5
    //cout<<b<<endl;//78.5
    cout<<c<<endl;//78.5
    cout<<d<<endl;//78.5
    return 0;
}

case 1:用返回值方式呼叫函式(如下圖,圖片來源:伯樂線上):

返回全域性變數temp的值時,C++會在記憶體中建立臨時變數並將temp的值拷貝給該臨時變數。當返回到主函式main後,賦值語句a=fn1(5.0)會把臨時變數的值再拷貝給變數a

case 2:用函式的返回值初始化引用的方式呼叫函式(如下圖,圖片來源:伯樂線上)

這種情況下,函式fn1()是以值方式返回到,返回時,首先拷貝temp的值給臨時變數。返回到主函式後,用臨時變數來初始化引用變數b,使得b成為該臨時變數到的別名。由於臨時變數的作用域短暫(在C++標準中,臨時變數或物件的生命週期在一個完整的語句表示式結束後便宣告結束,也就是在語句float &b=fn1(5.0);之後) ,所以b面臨無效的危險,很有可能以後的值是個無法確定的值。

 如果真的希望用函式的返回值來初始化一個引用,應當先建立一個變數,將函式的返回值賦給這個變數,然後再用該變數來初始化引用:

  int x=fn1(5.0);
  int &b=x;

 case 3:用返回引用的方式呼叫函式(如下圖,圖片來源:伯樂線上)

這種情況下,函式fn2()的返回值不產生副本,而是直接將變數temp返回給主函式,即主函式的賦值語句中的左值是直接從變數temp中拷貝而來(也就是說c只是變數temp的一個拷貝而非別名) ,這樣就避免了臨時變數的產生。尤其當變數temp是一個使用者自定義的類的物件時,這樣還避免了呼叫類中的拷貝建構函式在記憶體中建立臨時物件的過程,提高了程式的時間和空間的使用效率。

case 4:用函式返回的引用作為新引用的初始化值的方式來呼叫函式(如下圖,圖片來源:伯樂線上)

這種情況下,函式fn2()的返回值不產生副本,而是直接將變數temp返回給主函式。在主函式中,一個引用宣告d用該返回值初始化,也就是說此時d成為變數temp的別名。由於temp是全域性變數,所以在d的有效期內temp始終保持有效,故這種做法是安全的。

3.不能返回區域性變數的引用。如上面的例子,如果temp是區域性變數,那麼它會在函式返回後被銷燬,此時對temp的引用就會成為“無所指”的引用,程式會進入未知狀態。

4.不能返回函式內部通過new分配的記憶體的引用。雖然不存在區域性變數的被動銷燬問題,但如果被返回的函式的引用只是作為一個臨時變量出現,而沒有將其賦值給一個實際的變數,那麼就可能造成這個引用所指向的空間(有new分配)無法釋放的情況(由於沒有具體的變數名,故無法用delete手動釋放該記憶體),從而造成記憶體洩漏。因此應當避免這種情況的發生

5當返回類成員的引用時,最好是const引用。這樣可以避免在無意的情況下破壞該類的成員。

6.可以用函式返回的引用作為賦值表示式中的左值

#include<iostream>
using namespace std;
int value[10];
int error=-1;
int &func(int n){
    if(n>=0&&n<=9)
        return value[n];//返回的引用所繫結的變數一定是全域性變數,不能是函式中定義的區域性變數 
    else
        return error;
}

int main(){
    func(0)=10;
    func(4)=12;
    cout<<value[0]<<endl;
    cout<<value[4]<<endl;
    return 0; 
}

D.用引用實現多型

在C++中,引用是除了指標外另一個可以產生多型效果的手段。也就是說一個基類的引用可以用來繫結其派生類的例項

class Father;//基類(父類)
class Son:public Father{.....}//Son是Father的派生類
Son son;//son是類Son的一個例項
Father &ptr=son;//用派生類的物件初始化基類物件的使用

特別注意:

ptr只能用來訪問派生類物件中從基類繼承下來的成員如果基類(類Father)中定義的有虛擬函式,那麼就可以通過在派生類(類Son)中重寫這個虛擬函式來實現類的多型。