1. 程式人生 > >C++中返回引用和返回值的區別

C++中返回引用和返回值的區別

nbsp 反匯編 一個 的區別 tle cout span 編譯 匯編語言

轉自https://www.cnblogs.com/JMLiu/p/7928425.html

一、主要討論下面兩個函數的區別:

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

上面兩個函數,第一個返回值是int的引用int&,第二個返回值是int,二者的區別是什麽呢?

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

反匯編結果:

技術分享圖片
 1 #int& at()
 2 #{
 3 #    return m_data_;
 4 #}
 5 
 6 00BB6830  push        ebp  
 7 00BB6831  mov         ebp,esp  
 8 00BB6833  sub         esp,0CCh  
 9 00BB6839  push        ebx  
10 00BB683A  push        esi  
11 00BB683B  push        edi  
12 00BB683C  push        ecx  
13 00BB683D  lea         edi,[ebp-0CCh]  
14 00BB6843  mov         ecx,33h  
15 00BB6848  mov         eax,0CCCCCCCCh  
16 00BB684D  rep stos    dword ptr es:[edi]  
17 00BB684F  pop         ecx  
18 00BB6850  mov         dword ptr [this],ecx  
19         m_data_++;
20 00BB6853  mov         eax,dword ptr [this]  
21 00BB6856  mov         ecx,dword ptr [eax]  
22 00BB6858  add         ecx,1  
23 00BB685B  mov         edx,dword ptr [this]  
24 00BB685E  mov         dword ptr [edx],ecx  
25         return m_data_;
26 #取地址this中的值5879712(m_data_的地址)到寄存器eax中,此時寄存器eax存的是m_data_的地址
27 00BB6860  mov         eax,dword ptr [this]  
28     }
29 00BB6863  pop         edi  
30 00BB6864  pop         esi  
31 00BB6865  pop         ebx  
32 00BB6866  mov         esp,ebp  
33 00BB6868  pop         ebp  
34 00BB6869  ret  
35 
36 
37 
38 
39  
40     const int& a = mymay.at();    
41 00176AA2  lea         ecx,[mymay]  
42 00176AA5  call        MyMat::at (0171546h)  
43 #此時寄存器eax中的值為m_data_的地址5879712,直接將地址5879712存入地址a中。
44 00176AAA  mov         dword ptr [a],eax  
45     cout << a << endl;
技術分享圖片 技術分享圖片
 1 #int at()
 2 #{
 3 #    return m_data_;
 4 #}
 5 
 6 
 7 012B6830  push        ebp  
 8 012B6831  mov         ebp,esp  
 9 012B6833  sub         esp,0CCh  
10 012B6839  push        ebx  
11 012B683A  push        esi  
12 012B683B  push        edi  
13 012B683C  push        ecx  
14 012B683D  lea         edi,[ebp-0CCh]  
15 012B6843  mov         ecx,33h  
16 012B6848  mov         eax,0CCCCCCCCh  
17 012B684D  rep stos    dword ptr es:[edi]  
18 012B684F  pop         ecx  
19 012B6850  mov         dword ptr [this],ecx  
20         return m_data_;
21 #和上面一樣,也是先取出m_data_的地址
22 012B6853  mov         eax,dword ptr [this]
23 #和上面不一樣,不是直接將m_data_的地址放入寄存器eax中,而是取地址5879712中的值(m_data_=3)放入寄存器eax中,此時寄存器eax存的是m_data_的值(3)
24 012B6856  mov         eax,dword ptr [eax]  
25     }
26 012B6858  pop         edi  
27 012B6859  pop         esi  
28 012B685A  pop         ebx  
29 012B685B  mov         esp,ebp  
30 012B685D  pop         ebp  
31 012B685E  ret  
32 
33 
34 
35 
36   
37     const int& a = mymay.at();    
38 008E6AA2  lea         ecx,[mymay]  
39 008E6AA5  call        MyMat::at (08E154Bh) 
40 #此時eax的值為3,將3存入地址ebp-24h中,
41 008E6AAA  mov         dword ptr [ebp-24h],eax 
42 #將eax的值變成ebp-24h 
43 008E6AAD  lea         eax,[ebp-24h]  
44 #將地址ebp-24h寫到地址為a中,此時a代表的地址是ebp-24h
45 008E6AB0  mov         dword ptr [a],eax  
46     cout << a << endl;
技術分享圖片

所以結論就是:

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

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

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

二、說明一下函數返回時,如果不是返回一個變量的引用,則一定會生成一個臨時變量。

看下面的函數,返回的是t而不是&t,所以一定會有臨時變量產生。

1 T function1(){
2     T t(0);
3     return t;
4 }
5 T x=function1();

這裏的過程是:
1.創建命名對象t
2.拷貝構造一個無名的臨時對象,並返回這個臨時對象
3.由臨時對象拷貝構造對象x
4.T x=function1();這句語句結束時,析構臨時對象
這裏一共生成了3個對象,一個命名對象t,一個臨時對象作為返回值,一個命名對象x。

下面的函數稍微復雜一定,它沒有先定義一個中間變量t,看起來似乎是直接返回了一個臨時變量。但實際上,如果不經過c++的優化,那麽它並沒有提高效率,因為它還是創建了3個對象。

1 T function2(){
2      return T(0);
3 }
4 T x=function2();

這裏的過程是:
1.創建一個無名對象
2.由無名對象拷貝構造一個無名的臨時對象
3.析構無名對象,返回臨時對象
4.由臨時對象拷貝構造對象x
5.T x=function2()語句結束時,析構臨時對象。
這裏一共生成了3個對象,其中有2個對象都是馬上被析構掉的,不能被後面的代碼使用。既然是這樣,那麽就會有優化的余地,可以嘗試著不要前面的兩個臨時變量。c++確實會做這樣的優化,優化後的c++會避免匿名對象和臨時對象這兩個對象的生成,而直接生成x,這樣就減少了兩次對象生成-回收的消耗,提高了程序性能。

其實function1()這段代碼也是會經過優化的,但因為臨時對象t是一個命名對象,所以一定會被創建。存儲返回值的臨時對象是多余的,會被優化掉而不生成。
但是,程序員不應該依賴這種優化,因為c++不保證這種優化一定會做。

C++中返回引用和返回值的區別