




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第C++中stack的pop()函數(shù)返回值解析目錄stack的pop()函數(shù)返回值全部demo分析C++的返回值優(yōu)化從函數(shù)返回值RVO
stack的pop()函數(shù)返回值
inttemp=s.pop();
couttempendl;
運(yùn)行代碼會(huì)提示錯(cuò)誤:errorC2440:初始化:無法從void轉(zhuǎn)換為int
全部demo
#includeiostream
#includestack
usingnamespacestd;
intmain()
stackint
if(s.empty())
cout"empty"endl;//empty
s.push(1);
s.push(6);
s.push(66);
couts.size()endl;//3
inttemp=s.pop();
couttempendl;//66
couts.size()endl;//2
couts.top()endl;//6
couts.size()endl;//2
system("pause");
return0;
}
分析
C++中stack,其中有兩個(gè)方法:
pop(),返回void,top(),返回棧頂?shù)囊谩?/p>
所以想要提取棧頂元素,直接用s.top()
C++的返回值優(yōu)化
大家都知道過早的優(yōu)化是萬惡之源這句話,然而我相信其中的大多數(shù)人都不知道自己是不是在做過早的優(yōu)化。我也無法準(zhǔn)確的定義什么叫做過早的優(yōu)化,但我相信這過早的優(yōu)化要么是得不償失的,要么干脆是有害無利的。今天我就想舉個(gè)我認(rèn)為是過早的優(yōu)化的例子。
從函數(shù)返回值
為了從一個(gè)函數(shù)得到運(yùn)行結(jié)果,常規(guī)的途徑有兩個(gè):通過返回值和通過傳入函數(shù)的引用或指針(當(dāng)然還可以通過全局變量或成員變量,但我覺得這算不上是什么好主意)。
通過傳給函數(shù)一個(gè)引用或指針來承載返回值在很多情況下是無可厚非的,畢竟有時(shí)函數(shù)需要將多個(gè)值返回給用戶。除了這種情況之外,我覺得應(yīng)當(dāng)盡量做到參數(shù)作為函數(shù)輸入,返回值作為函數(shù)輸出(這不是很自然的事情嗎?)。然而,我們總能看到一些突破常規(guī)的做法:
首先定義Message類:
structMessage
inta;
intb;
intc;
intd;
inte;
intf;
};
為了從某個(gè)地方(比如一個(gè)隊(duì)列)得到一個(gè)特定Message對(duì)象,有些人喜歡寫一個(gè)這樣的getMessage:
voidgetMessage(Messagemsg);//形式1
雖然只有一個(gè)返回值,但仍然是通過傳入函數(shù)的引用返回給調(diào)用者的。
為什么要這樣呢?嗯,為了提高性能。你知道,要是這樣定義函數(shù),返回Message對(duì)象時(shí)必須要構(gòu)造一個(gè)臨時(shí)對(duì)象,這對(duì)性能有影響。
MessagegetMessage();//形式2
我們先不討論這帶來了多少性能提升,先看看形式1相對(duì)形式2帶來了哪些弊端。我認(rèn)為有兩點(diǎn):
1.可讀性變差
略(我希望你能和我一樣認(rèn)為這是顯而易見的)。
2.將對(duì)象的初始化劃分成了兩個(gè)步驟
調(diào)用形式1時(shí),你必然要這樣:
Messagemsg;
//S1
getMessage(msg);//S2
這給維護(hù)者帶來了犯錯(cuò)的機(jī)會(huì):一些需要在S2語句后面對(duì)msg進(jìn)行的操作有可能會(huì)被錯(cuò)誤的放在S1和S2之間。
如果是形式2,維護(hù)者就不可能犯這種錯(cuò)誤:
Messagemsg=getMessage();
好,現(xiàn)在我們來看性能,形式2真的相對(duì)形式1性能更差嗎?對(duì)于下面的代碼:
#includestdio.h
structMessage
Message()
{
printf("Message::Message()iscalled\n");
}
Message(constMessage)
{
printf("Message::Message(constMessagemsg)iscalled\n");
}
Messageoperator=(constMessage)
{
printf("Message::operator=(constMessage)iscalled\n");
}
~Message()
{
printf("Message::~Message()iscalled\n");
}
inta;
intb;
intc;
intd;
inte;
intf;
MessagegetMessage()
Messageresult;
result.a=0x11111111;
returnresult;
intmain()
Messagemsg=getMessage();
return0;
}
你認(rèn)為運(yùn)行時(shí)會(huì)輸出什么呢?是不是這樣:
Message::Message()iscalled
Message::Message(constMessagemsg)iscalled
Message::~Message()iscalled
Message::~Message()iscalled
并沒有像預(yù)期的輸出那樣。
如果使用MSVC2017編譯,且關(guān)閉優(yōu)化(/Od),確實(shí)可以得到預(yù)期輸入,但是一旦打開優(yōu)化(/O2),輸出就和GCC的一樣了。
我們看看實(shí)際上生成了什么代碼(使用GCC編譯):
(gdb)disassemblemain
Dumpofassemblercodeforfunctionmain():
0x0000000000000776+0:
push
%rbp
0x0000000000000777+1:
mov
%rsp,%rbp
0x000000000000077a+4:
push
%rbx
0x000000000000077b+5:
sub
$0x28,%rsp
0x000000000000077f+9:
mov
%fs:0x28,%rax
0x0000000000000788+18:
mov
%rax,-0x18(%rbp)
0x000000000000078c+22:
xor
%eax,%eax
0x000000000000078e+24:
lea
-0x30(%rbp),%rax
#將棧上地址-0x30(%rbp)傳給getMessage函數(shù)
0x0000000000000792+28:
mov
%rax,%rdi
0x0000000000000795+31:
callq
0x72agetMessage()
0x000000000000079a+36:
mov
$0x0,%ebx
0x000000000000079f+41:
lea
-0x30(%rbp),%rax
0x00000000000007a3+45:
mov
%rax,%rdi
0x00000000000007a6+48:
callq
0x7e4Message::~Message()
0x00000000000007ab+53:
mov
%ebx,%eax
0x00000000000007ad+55:
mov
-0x18(%rbp),%rdx
0x00000000000007b1+59:
xor
%fs:0x28,%rdx
0x00000000000007ba+68:
je
0x7c1main()+75
0x00000000000007bc+70:
callq
0x5f0__stack_chk_fail@plt
0x00000000000007c1+75:
add
$0x28,%rsp
0x00000000000007c5+79:
pop
%rbx
0x00000000000007c6+80:
pop
%rbp
0x00000000000007c7+81:
retq
Endofassemblerdump.
(gdb)disassemblegetMessage
DumpofassemblercodeforfunctiongetMessage():
0x000000000000072a+0:
push
%rbp
0x000000000000072b+1:
mov
%rsp,%rbp
0x000000000000072e+4:
sub
$0x20,%rsp
0x0000000000000732+8:
mov
%rdi,-0x18(%rbp)
#將main函數(shù)傳入的棧上地址保存到-0x18(%rbp)處
0x0000000000000736+12:
mov
%fs:0x28,%rax
0x000000000000073f+21:
mov
%rax,-0x8(%rbp)
0x0000000000000743+25:
xor
%eax,%eax
0x0000000000000745+27:
mov
-0x18(%rbp),%rax
#將main函數(shù)傳入的棧上地址傳給Message::Message()函數(shù)
0x0000000000000749+31:
mov
%rax,%rdi
0x000000000000074c+34:
callq
0x7c8Message::Message()
0x0000000000000751+39:
mov
-0x18(%rbp),%rax
0x0000000000000755+43:
movl
$0x11111111,(%rax)
0x000000000000075b+49:
nop
0x000000000000075c+50:
mov
-0x18(%rbp),%rax
0x0000000000000760+54:
mov
-0x8(%rbp),%rdx
0x0000000000000764+58:
xor
%fs:0x28,%rdx
0x000000000000076d+67:
je
0x774getMessage()+74
0x000000000000076f+69:
callq
0x5f0__stack_chk_fail@plt
0x0000000000000774+74:
leaveq
0x0000000000000775+75:
retq
Endofassemblerdump.
可以看出來,在getMessage函數(shù)中構(gòu)造的對(duì)象實(shí)際上位于main函數(shù)的棧幀上,并沒有額外構(gòu)造一個(gè)Message對(duì)象。這是因?yàn)殚_啟了所謂的返回值優(yōu)化(RVO,ReturnValueOptimization)的緣故。你想得到的效果編譯器已經(jīng)自動(dòng)幫你完成了,你不必再犧牲什么。
RVO
對(duì)于我們這些用戶來說,RVO并不是什么特別復(fù)雜的機(jī)制,主流的GCC和MSVC均支持,也沒什么特別需要注意的地方。它存在的目的是優(yōu)化掉不必要的拷貝復(fù)制函數(shù)的調(diào)用,即使拷貝復(fù)制函數(shù)有什么副作用,例如上面代碼中的打印語句,這可能是唯一需要注意的地方了。從上面的匯編代碼中可以看出來,在GCC中,其基本手段是直接將返回的對(duì)象構(gòu)造在調(diào)用者棧幀上,這樣調(diào)用者就可以直接訪問這個(gè)對(duì)象而不必復(fù)制。
RVO是有限制條件的,在某些情況下無法進(jìn)行優(yōu)化,在一篇關(guān)于MSVC2005的RVO技術(shù)的文章中,提到了3點(diǎn)導(dǎo)致無法優(yōu)化的情況:
1.函數(shù)拋異常
關(guān)于這點(diǎn),我是有疑問的。文章中說如果函數(shù)拋異常,開不開RVO結(jié)果都一樣。如果函數(shù)拋異常,無法正常的返回,我當(dāng)然不會(huì)要求編譯器去做RVO了。
2.函數(shù)可能返回具有不同變量名的對(duì)象
MessagegetMessage_NoRVO1(intin)
Messagemsg1;
msg1.a=1;
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 種植蔬菜合同協(xié)議書
- 齒輪傳動(dòng)考試題及答案
- 農(nóng)村房子開發(fā)合同協(xié)議書
- 事故處理合同協(xié)議書范本
- 天工合同協(xié)議書
- 股權(quán)合同協(xié)議書飯店
- 婚前協(xié)議書合同書
- 就業(yè)協(xié)議書與勞動(dòng)合同的異同
- 木門簽訂合同協(xié)議書
- 保潔工合同協(xié)議書
- 2025-2030中國船舶行業(yè)發(fā)展分析及發(fā)展前景與投資研究報(bào)告
- 耐藥菌耐藥性監(jiān)測策略-全面剖析
- 2025年中國陳皮市場調(diào)查研究報(bào)告
- 2024年農(nóng)藝師考試考試形式試題及答案
- 老年綜合評(píng)估技術(shù)應(yīng)用中國專家共識(shí)解讀
- 北京市通州區(qū)2025年初中學(xué)業(yè)水平模擬考試(一模)英語試卷(含答案)
- 手術(shù)中大出血搶救流程
- 初中語文第23課《“蛟龍”探?!氛n件-2024-2025學(xué)年統(tǒng)編版語文七年級(jí)下冊(cè)
- 2025重慶武工工業(yè)技術(shù)研究院有限公司招聘15人筆試參考題庫附帶答案詳解
- 電工技術(shù)基礎(chǔ) 教案全套 歐小東 第1-10章 直流電路的基礎(chǔ)知識(shí)-過渡過程
- 汽車銷售禮儀與溝通技巧考核試卷
評(píng)論
0/150
提交評(píng)論