不同功能的函數其內部實現各不相同,看起來似乎無法就“內部實現”達成一致的觀點。但根據經驗,我們可以在函數體的“入口處”和“出口處”從嚴把關,從而提高函數的質量。
?
l???????? 【規則6-3-1】在函數體的“入口處”,對參數的有效性進行檢查。
很多程序錯誤是由非法參數引起的,我們應該充分理解并正確使用“斷言”(assert)來防止此類錯誤。詳見6.5節“使用斷言”。
?
l???????? 【規則6-3-2】在函數體的“出口處”,對return語句的正確性和效率進行檢查。
?? ?如果函數有返回值,那么函數的“出口處”是return語句。我們不要輕視return語句。如果return語句寫得不好,函數要么出錯,要么效率低下。
注意事項如下:
(1)return語句不可返回指向“棧內存”的“指針”或者“引用”,因為該內存在函數體結束時被自動銷毀。例如
??? char * Func(void)
??? {
??????? char str[] = “hello world”; // str的內存位于棧上
??????? …
??? ?? return str;??????? // 將導致錯誤
??? }
(2)要搞清楚返回的究竟是“值”、“指針”還是“引用”。
(3)如果函數返回值是一個對象,要考慮return語句的效率。例如???
?????? ?????? return String(s1 + s2);
這是臨時對象的語法,表示“創建一個臨時對象并返回它”。不要以為它與“先創建一個局部對象temp并返回它的結果”是等價的,如
String temp(s1 + s2);
return temp;
實質不然,上述代碼將發生三件事。首先,temp對象被創建,同時完成初始化;然后拷貝構造函數把temp拷貝到保存返回值的外部存儲單元中;最后,temp在函數結束時被銷毀(調用析構函數)。然而“創建一個臨時對象并返回它”的過程是不同的,編譯器直接把臨時對象創建并初始化在外部存儲單元中,省去了拷貝和析構的化費,提高了效率。
類似地,我們不要將?
return int(x + y); // 創建一個臨時變量并返回它
寫成
int temp = x + y;
return temp;
由于內部數據類型如int,float,double的變量不存在構造函數與析構函數,雖然該“臨時變量的語法”不會提高多少效率,但是程序更加簡潔易讀。
首先來看內部數據類型:
return int(x+y)
與
int temp = x+y;
return temp;
的區別:
反匯編如下:
? return ? int(x+y); ? ? ? ? ? ? ? ? ? ? ? mov ? eax,x ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?add ? eax,y ?
? ?
? ?
? int ? temp ? = ? x ? + ? y; ? ? ? ? ??mov ? eax ? ,x ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??add ? eax ? ,y ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mov ? temp,eax ?
? ?
? return ? temp; ? ? ? ? ? ? ? ? ? ? ????? mov ? eax,temp ?
?
可見:對于前者編譯器直接將x+Y的值放如了寄存器eax中,而對于后者我們可以通過紅色反匯編部分,相對前者對復制了2次(從eaxàtemp,再從tempàeax).
?
再看類類型
return string (x+y)
與
string temp = x+y;
return temp;
的區別:
對于前者: 編譯器直接把臨時對象創建并初始化在外部存儲單元中,省去了拷貝和析構的化費,提高了效率
?
138:????? String fun()
139:????? {
0040EC70?? push??????? ebp
0040EC71?? mov???????? ebp,esp
0040EC73?? push??????? 0FFh
0040EC75?? push??????? offset __ehhandler$?fun@@YA?AVString@@XZ (004132f2)
0040EC7A?? mov???????? eax,fs:[00000000]
0040EC80?? push??????? eax
0040EC81?? mov???????? dword ptr fs:[0],esp
0040EC88?? sub???????? esp,58h
0040EC8B?? push??????? ebx
0040EC8C?? push??????? esi
0040EC8D?? push???????edi
0040EC8E?? lea???????? edi,[ebp-64h]
0040EC91?? mov???????? ecx,16h
0040EC96?? mov???????? eax,0CCCCCCCCh
0040EC9B?? rep stos??? dword ptr [edi]
0040EC9D?? mov???????? dword ptr [ebp-1Ch],0
140:
141:???????? String x;
0040ECA4?? push??????? 0
0040ECA6?? lea???????? ecx,[ebp-10h]
0040ECA9?? call??????? @ILT+20(String::String) (00401019)
//x
對象
constructor
0040ECAE?? mov???????? dword ptr [ebp-4],1
142:???????? String y;
0040ECB5?? push??????? 0
0040ECB7?? lea???????? ecx,[ebp-14h]
0040ECBA?? call??? ????@ILT+20(String::String) (00401019)//y對象constructor
0040ECBF?? mov???????? byte ptr [ebp-4],2
143:???????? return String(x+y);
0040ECC3?? lea???????? eax,[ebp-14h]
0040ECC6?? push??????? eax
0040ECC7?? lea???????? ecx,[ebp-10h]
0040ECCA?? push??????? ecx
0040ECCB?? lea???????? edx,[ebp-18h]
0040ECCE?? push??????? edx
0040ECCF?? call??????? @ILT+25(operator+) (0040101e)//調用operator+
0040ECD4?? add???????? esp,0Ch
0040ECD7?? mov???????? dword ptr [ebp-20h],eax
0040ECDA?? mov???????? eax,dword ptr [ebp-20h]
0040ECDD?? mov???????? dword ptr [ebp-24h],eax
0040ECE0?? mov???????? byte ptr [ebp-4],3
0040ECE4?? mov???????? ecx,dword ptr [ebp-24h]
0040ECE7?? push??????? ecx
0040ECE8?? mov???????? ecx,dword ptr [ebp+8]
0040ECEB?? call??????? @ILT+15(String::String) (00401014) //constructor臨時對象
0040ECF0?? mov???????? edx,dword ptr [ebp-1Ch]
0040ECF3?? or????????? edx,1
0040ECF6?? mov???????? dword ptr [ebp-1Ch],edx
0040ECF9?? mov???????? byte ptr [ebp-4],2
0040ECFD?? lea??????? ?ecx,[ebp-18h]
0040ED00?? call??????? @ILT+5(String::~String) (0040100a)//y對象destructor
0040ED05?? mov???????? byte ptr [ebp-4],1
0040ED09?? lea???????? ecx,[ebp-14h]
0040ED0C?? call??????? @ILT+5(String::~String) (0040100a)//x對象destructor
0040ED11?? mov???????? byte ptr [ebp-4],0
0040ED15?? lea???????? ecx,[ebp-10h]
0040ED18?? call??????? @ILT+5(String::~String) (0040100a) //destructor? 銷毀臨時對象
0040ED1D?? mov???????? eax,dword ptr [ebp+8]
144:????? };
?
對于后者: 上述代碼將發生三件事。首先,temp對象被創建,同時完成初始化;然后拷貝構造函數把temp拷貝到保存返回值的外部存儲單元中;最后,temp在函數結束時被銷毀(調用析構函數)。
138:????? String fun()
139:????? {
0040ED80?? push??????? ebp
0040ED81?? mov???????? ebp,esp
0040ED83?? push??????? 0FFh
0040ED85?? push??????? offset __ehhandler$?fun@@YA?AVString@@XZ (004132fb)
0040ED8A?? mov???????? eax,fs:[00000000]
0040ED90?? push??????? eax
0040ED91?? mov???????? dword ptr fs:[0],esp
0040ED98?? sub???????? esp,5Ch
0040ED9B?? push??????? ebx
0040ED9C?? push??????? esi
0040ED9D?? push??????? edi
0040ED9E?? lea???????? edi,[ebp-68h]
0040EDA1?? mov???????? ecx,17h
0040EDA6?? mov???????? eax,0CCCCCCCCh
0040EDAB?? rep stos??? dword ptr [edi]
0040EDAD?? mov???????? dword ptr [ebp-20h],0
140:
141:???????? String x;
0040EDB4?? push??????? 0
0040EDB6?? lea???????? ecx,[ebp-10h]
0040EDB9?? call??????? @ILT+20(String::String) (00401019)//x對象constructor
0040EDBE?? mov???????? dword ptr [ebp-4],1
142:???????? String y;
0040EDC5?? push??????? 0
0040EDC7?? lea???????? ecx,[ebp-14h]
0040EDCA?? call??????? @ILT+20(String::String) (00401019)?//y對象constructor
?
0040EDCF?? mov???????? byte ptr [ebp-4],2
143:???????? String temp;
0040EDD3?? push??????? 0
0040EDD5?? lea???????? ecx,[ebp-18h]
0040EDD8?? call??????? @ILT+20(String::String) (00401019)//temp對象constructor
0040EDDD?? mov???????? byte ptr [ebp-4],3
144:???????? return temp=x+y;
0040EDE1?? lea???????? eax,[ebp-14h]
0040EDE4?? push??????? eax
0040EDE5?? lea???????? ecx,[ebp-10h]
0040EDE8?? push??????? ecx
0040EDE9?? lea???????? edx,[ebp-1Ch]
0040EDEC?? push??????? edx
0040EDED?? call??????? @ILT+25(operator+) (0040101e)//調用operator+
0040EDF2?? add???????? esp,0Ch
0040EDF5?? mov???????? dword ptr [ebp-24h],eax
0040EDF8?? mov???????? eax,dword ptr [ebp-24h]
0040EDFB?? mov???????? dword ptr [ebp-28h],eax
0040EDFE?? mov???????? byte ptr [ebp-4],4
0040EE02?? mov???????? ecx,dword ptr [ebp-28h]
0040EE05?? push??????? ecx
0040EE06?? lea???????? ecx,[ebp-18h]
0040EE09?? call??????? @ILT+0(String::operator=) (00401005)??//調用operator=
0040EE0E?? push??????? eax
0040EE0F?? mov???????? ecx,dword ptr [ebp+8]
0040EE12?? call??????? @ILT+15(String::String) (00401014) //constructor臨時對象
0040EE17?? mov???????? edx,dword ptr [ebp-20h]
0040EE1A?? or????????? edx,1
0040EE1D?? mov???????? dword ptr [ebp-20h],edx
0040EE20?? mov???????? byte ptr [ebp-4],3
0040EE24?? lea???????? ecx,[ebp-1Ch]
0040EE27?? call??????? @ILT+5(String::~String) (0040100a)//temp對象destructor
0040EE2C?? mov???????? byte ptr [ebp-4],2
0040EE30?? lea???????? ecx,[ebp-18h]
0040EE33?? call??????? @ILT+5(String::~String) (0040100a)//y對象destructor
0040EE38?? mov???????? byte ptr [ebp-4],1
0040EE3C?? lea???????? ecx,[ebp-14h]
0040EE3F?? call??????? @ILT+5(String::~String) (0040100a)//x對象destructor
0040EE44?? mov???????? byte ptr [ebp-4],0
0040EE48?? lea???????? ecx,[ebp-10h]
0040EE4B?? call??????? @ILT+5(String::~String) (0040100a) //destructor銷毀臨時對象
0040EE50?? mov???????? eax,dword ptr [ebp+8]
145:?? ???};
?
可見,兩中return方式都會有臨時對象的產生,而不同在于String temp; retrun temp=x+y方式多了三個步驟.(即紅色注釋), temp對象調用constructor, 調用operator=, temp對象調用destructor.所以說return String(x+y)效率比較高
?
順便問大蝦們個問題
:
這段反匯編中
,
臨時對象是放在什么地方的呀
??
?
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1394353