C++中stack的pop()函數(shù)返回值解析_第1頁
C++中stack的pop()函數(shù)返回值解析_第2頁
C++中stack的pop()函數(shù)返回值解析_第3頁
C++中stack的pop()函數(shù)返回值解析_第4頁
C++中stack的pop()函數(shù)返回值解析_第5頁
已閱讀5頁,還剩5頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論