欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++?引用作為函數(shù)的返回值方式

 更新時間:2022年07月13日 09:29:19   作者:Jeff_  
這篇文章主要介紹了C/C++?引用作為函數(shù)的返回值方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

語法:類型 &函數(shù)名(形參列表){ 函數(shù)體 }

特別注意:

1.引用作為函數(shù)的返回值時,必須在定義函數(shù)時在函數(shù)名前將&

2.用引用作函數(shù)的返回值的最大的好處是在內(nèi)存中不產(chǎn)生返回值的副本

//代碼來源: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:用函數(shù)的返回值作為引用的初始化值 [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:用函數(shù)返回的引用作為新引用的初始化值
    cout<<a<<endl;//78.5
    //cout<<b<<endl;//78.5
    cout<<c<<endl;//78.5
    cout<<d<<endl;//78.5
    return 0;
}

case1:用返回值方式調(diào)用函數(shù)

如下圖:

返回全局變量temp的值時,C++會在內(nèi)存中創(chuàng)建臨時變量并將temp的值拷貝給該臨時變量。當(dāng)返回到主函數(shù)main后,賦值語句a=fn1(5.0)會把臨時變量的值再拷貝給變量a

case2:用函數(shù)的返回值初始化引用的方式調(diào)用函數(shù)

如下圖:

這種情況下,函數(shù)fn1()是以值方式返回到,返回時,首先拷貝temp的值給臨時變量。

返回到主函數(shù)后,用臨時變量來初始化引用變量b,使得b成為該臨時變量到的別名。

由于臨時變量的作用域短暫(在C++標(biāo)準(zhǔn)中,臨時變量或?qū)ο蟮纳芷谠谝粋€完整的語句表達式結(jié)束后便宣告結(jié)束,也就是在語句float &b=fn1(5.0);之后),所以b面臨無效的危險,很有可能以后的值是個無法確定的值。

 如果真的希望用函數(shù)的返回值來初始化一個引用,應(yīng)當(dāng)先創(chuàng)建一個變量,將函數(shù)的返回值賦給這個變量,然后再用該變量來初始化引用:

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

case3:用返回引用的方式調(diào)用函數(shù)

如下圖:

這種情況下,函數(shù)fn2()的返回值不產(chǎn)生副本,而是直接將變量temp返回給主函數(shù),即主函數(shù)的賦值語句中的左值是直接從變量temp中拷貝而來(也就是說c只是變量temp的一個拷貝而非別名) ,這樣就避免了臨時變量的產(chǎn)生。尤其當(dāng)變量temp是一個用戶自定義的類的對象時,這樣還避免了調(diào)用類中的拷貝構(gòu)造函數(shù)在內(nèi)存中創(chuàng)建臨時對象的過程,提高了程序的時間和空間的使用效率。

case4:用函數(shù)返回的引用作為新引用的初始化值的方式來調(diào)用函數(shù)

如下圖:

這種情況下,函數(shù)fn2()的返回值不產(chǎn)生副本,而是直接將變量temp返回給主函數(shù)。

1.在主函數(shù)中,一個引用聲明d用該返回值初始化,也就是說此時d成為變量temp的別名。

2.由于temp是全局變量,所以在d的有效期內(nèi)temp始終保持有效,故這種做法是安全的。

3.不能返回局部變量的引用。如上面的例子,如果temp是局部變量,那么它會在函數(shù)返回后被銷毀,此時對temp的引用就會成為“無所指”的引用,程序會進入未知狀態(tài)。

4.不能返回函數(shù)內(nèi)部通過new分配的內(nèi)存的引用。雖然不存在局部變量的被動銷毀問題,但如果被返回的函數(shù)的引用只是作為一個臨時變量出現(xiàn),而沒有將其賦值給一個實際的變量,那么就可能造成這個引用所指向的空間(有new分配)無法釋放的情況(由于沒有具體的變量名,故無法用delete手動釋放該內(nèi)存),從而造成內(nèi)存泄漏。因此應(yīng)當(dāng)避免這種情況的發(fā)生

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

6.可以用函數(shù)返回的引用作為賦值表達式中的左值

#include<iostream>
using namespace std;
int value[10];
int error=-1;
int &func(int n){
    if(n>=0&&n<=9)
        return value[n];//返回的引用所綁定的變量一定是全局變量,不能是函數(shù)中定義的局部變量 
    else
        return error;
}
 
int main(){
    func(0)=10;
    func(4)=12;
    cout<<value[0]<<endl;
    cout<<value[4]<<endl;
    return 0; 
}

用引用實現(xiàn)多態(tài)

在C++中,引用是除了指針外另一個可以產(chǎn)生多態(tài)效果的手段。

也就是說一個基類的引用可以用來綁定其派生類的實例

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

特別注意:

ptr只能用來訪問派生類對象中從基類繼承下來的成員。如果基類(類Father)中定義的有虛函數(shù),那么就可以通過在派生類(類Son)中重寫這個虛函數(shù)來實現(xiàn)類的多態(tài)。

函數(shù)中返回引用和返回值的區(qū)別

主要討論下面兩個函數(shù)的區(qū)別

int& at()
{
? ? return m_data_;
}
int at()
{
? ? return m_data_;
}

上面兩個函數(shù),第一個返回值是int的引用int&,第二個返回值是int,二者的區(qū)別是什么呢?

我們先用一個語句 const int& a = mymay.at(); 來分別調(diào)用一次上面兩個函數(shù),然后看匯編語言的結(jié)果。

反匯編結(jié)果:

#int& at()
#{
#    return m_data_;
#}
00BB6830  push        ebp
00BB6831  mov         ebp,esp
00BB6833  sub         esp,0CCh
00BB6839  push        ebx
00BB683A  push        esi
00BB683B  push        edi
00BB683C  push        ecx
00BB683D  lea         edi,[ebp-0CCh]
00BB6843  mov         ecx,33h
00BB6848  mov         eax,0CCCCCCCCh
00BB684D  rep stos    dword ptr es:[edi]
00BB684F  pop         ecx
00BB6850  mov         dword ptr [this],ecx
        m_data_++;
00BB6853  mov         eax,dword ptr [this]
00BB6856  mov         ecx,dword ptr [eax]
00BB6858  add         ecx,1
00BB685B  mov         edx,dword ptr [this]
00BB685E  mov         dword ptr [edx],ecx
        return m_data_;
#取地址this中的值5879712(m_data_的地址)到寄存器eax中,此時寄存器eax存的是m_data_的地址
00BB6860  mov         eax,dword ptr [this]
    }
00BB6863  pop         edi
00BB6864  pop         esi
00BB6865  pop         ebx
00BB6866  mov         esp,ebp
00BB6868  pop         ebp
00BB6869  ret
    const int& a = mymay.at();
00176AA2  lea         ecx,[mymay]
00176AA5  call        MyMat::at (0171546h)
#此時寄存器eax中的值為m_data_的地址5879712,直接將地址5879712存入地址a中。
00176AAA  mov         dword ptr [a],eax
    cout << a << endl;
#int at()
#{
#    return m_data_;
#}
012B6830  push        ebp
012B6831  mov         ebp,esp
012B6833  sub         esp,0CCh
012B6839  push        ebx
012B683A  push        esi
012B683B  push        edi
012B683C  push        ecx
012B683D  lea         edi,[ebp-0CCh]
012B6843  mov         ecx,33h
012B6848  mov         eax,0CCCCCCCCh
012B684D  rep stos    dword ptr es:[edi]
012B684F  pop         ecx
012B6850  mov         dword ptr [this],ecx
        return m_data_;
#和上面一樣,也是先取出m_data_的地址
012B6853  mov         eax,dword ptr [this]
#和上面不一樣,不是直接將m_data_的地址放入寄存器eax中,而是取地址5879712中的值(m_data_=3)放入寄存器eax中,此時寄存器eax存的是m_data_的值(3)
012B6856  mov         eax,dword ptr [eax]
    }
012B6858  pop         edi
012B6859  pop         esi
012B685A  pop         ebx
012B685B  mov         esp,ebp
012B685D  pop         ebp
012B685E  ret
    const int& a = mymay.at();
008E6AA2  lea         ecx,[mymay]
008E6AA5  call        MyMat::at (08E154Bh)
#此時eax的值為3,將3存入地址ebp-24h中,
008E6AAA  mov         dword ptr [ebp-24h],eax
#將eax的值變成ebp-24h
008E6AAD  lea         eax,[ebp-24h]
#將地址ebp-24h寫到地址為a中,此時a代表的地址是ebp-24h
008E6AB0  mov         dword ptr [a],eax
    cout << a << endl;

所以結(jié)論就是:

1、返回值為引用型(int& )的時候,返回的是地址,因為這里用的是 int& a=mymay.at(); ,所以a和m_data_指的是同一塊地址(由寄存器eax傳回的5879712)。

2、返回值不是引用型(int)的時候,返回的是一個數(shù)值。這個時候就很有意思了,編譯器是先將這個數(shù)值放入一個內(nèi)存中(上面例子中,該內(nèi)存地址為ebp-24h),再把這個地址付給a,此時的a代表的地址是ebp-24h,和m_data_代表的地址不一樣(m_data_代表的地址是5879712)。

3、綜上兩點可以看出,當(dāng)返回的值不是引用型時,編譯器會專門給返回值分配出一塊內(nèi)存的(例子中為ebp-24h)。

說明一下函數(shù)返回時

如果不是返回一個變量的引用,則一定會生成一個臨時變量。

看下面的函數(shù),返回的是t而不是&t,所以一定會有臨時變量產(chǎn)生。

?T function1(){
? ? ?T t(0);
? ? ?return t;
?}
?T x=function1();

這里的過程是:

1.創(chuàng)建命名對象t

2.拷貝構(gòu)造一個無名的臨時對象,并返回這個臨時對象

3.由臨時對象拷貝構(gòu)造對象x

4.T x=function1();這句語句結(jié)束時,析構(gòu)臨時對象

這里一共生成了3個對象,一個命名對象t,一個臨時對象作為返回值,一個命名對象x。

下面的函數(shù)稍微復(fù)雜一定,它沒有先定義一個中間變量t,看起來似乎是直接返回了一個臨時變量。但實際上,如果不經(jīng)過c++的優(yōu)化,那么它并沒有提高效率,因為它還是創(chuàng)建了3個對象。

?T function2(){
? ? ? return T(0);
?}
?T x=function2();

這里的過程是:

1.創(chuàng)建一個無名對象

2.由無名對象拷貝構(gòu)造一個無名的臨時對象

3.析構(gòu)無名對象,返回臨時對象

4.由臨時對象拷貝構(gòu)造對象x

5.T x=function2()語句結(jié)束時,析構(gòu)臨時對象。

這里一共生成了3個對象,其中有2個對象都是馬上被析構(gòu)掉的,不能被后面的代碼使用。既然是這樣,那么就會有優(yōu)化的余地,可以嘗試著不要前面的兩個臨時變量。c++確實會做這樣的優(yōu)化,優(yōu)化后的c++會避免匿名對象和臨時對象這兩個對象的生成,而直接生成x,這樣就減少了兩次對象生成-回收的消耗,提高了程序性能。

其實function1()這段代碼也是會經(jīng)過優(yōu)化的,但因為臨時對象t是一個命名對象,所以一定會被創(chuàng)建。存儲返回值的臨時對象是多余的,會被優(yōu)化掉而不生成。

但是,程序員不應(yīng)該依賴這種優(yōu)化,因為c++不保證這種優(yōu)化一定會做。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論