最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++中stack的pop()函數(shù)返回值解析

 更新時(shí)間:2022年07月25日 14:42:33   作者:code的魅力  
這篇文章主要介紹了C++中stack的pop()函數(shù)返回值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

stack的pop()函數(shù)返回值

    int temp = s.pop();
    cout<<temp<<endl; 

運(yùn)行代碼會(huì)提示錯(cuò)誤:error C2440: “初始化”: 無(wú)法從“void”轉(zhuǎn)換為“int”

全部demo

#include <iostream>
#include <stack>
 
using namespace std;
 
int main()
{
	stack<int> s;
	if(s.empty())
		cout<<"empty"<<endl;   //empty
	s.push(1);
	s.push(6);
	s.push(66);
	cout<<s.size()<<endl;   //3
	int temp = s.pop();
	cout<<temp<<endl;	//66
	cout<<s.size()<<endl;	//2
	cout<<s.top()<<endl;	//6
	cout<<s.size()<<endl;	//2
	system("pause");
	return 0;
 
}

分析

C++中stack,其中有兩個(gè)方法:

  • pop(), 返回void,
  • top(),返回棧頂?shù)囊谩?/li>

所以想要提取棧頂元素,直接用s.top()

C++的返回值優(yōu)化

大家都知道“過(guò)早的優(yōu)化是萬(wàn)惡之源”這句話,然而我相信其中的大多數(shù)人都不知道自己是不是在做過(guò)早的優(yōu)化。我也無(wú)法準(zhǔn)確的定義什么叫做“過(guò)早的優(yōu)化”,但我相信這“過(guò)早的優(yōu)化”要么是得不償失的,要么干脆是有害無(wú)利的。今天我就想舉個(gè)我認(rèn)為是“過(guò)早的優(yōu)化”的例子。

從函數(shù)返回值

為了從一個(gè)函數(shù)得到運(yùn)行結(jié)果,常規(guī)的途徑有兩個(gè):通過(guò)返回值和通過(guò)傳入函數(shù)的引用或指針(當(dāng)然還可以通過(guò)全局變量或成員變量,但我覺得這算不上是什么好主意)。

通過(guò)傳給函數(shù)一個(gè)引用或指針來(lái)承載返回值在很多情況下是無(wú)可厚非的,畢竟有時(shí)函數(shù)需要將多個(gè)值返回給用戶。除了這種情況之外,我覺得應(yīng)當(dāng)盡量做到參數(shù)作為函數(shù)輸入,返回值作為函數(shù)輸出(這不是很自然的事情嗎?)。然而,我們總能看到一些“突破常規(guī)”的做法:

首先定義Message類:

struct Message
{
? ? int a;
? ? int b;
? ? int c;
? ? int d;
? ? int e;
? ? int f;
};

為了從某個(gè)地方(比如一個(gè)隊(duì)列)得到一個(gè)特定Message對(duì)象,有些人喜歡寫一個(gè)這樣的getMessage:

void getMessage(Message &msg); // 形式1

雖然只有一個(gè)返回值,但仍然是通過(guò)傳入函數(shù)的引用返回給調(diào)用者的。

為什么要這樣呢?“嗯,為了提高性能。你知道,要是這樣定義函數(shù),返回Message對(duì)象時(shí)必須要構(gòu)造一個(gè)臨時(shí)對(duì)象,這對(duì)性能有影響。”

Message getMessage(); // 形式2

我們先不討論這帶來(lái)了多少性能提升,先看看形式1相對(duì)形式2帶來(lái)了哪些弊端。我認(rèn)為有兩點(diǎn):

1. 可讀性變差

略(我希望你能和我一樣認(rèn)為這是顯而易見的)。

2. 將對(duì)象的初始化劃分成了兩個(gè)步驟

調(diào)用形式1時(shí),你必然要這樣:

Message msg; ? ? // S1
getMessage(msg); // S2

這給維護(hù)者帶來(lái)了犯錯(cuò)的機(jī)會(huì):一些需要在S2語(yǔ)句后面對(duì)msg進(jìn)行的操作有可能會(huì)被錯(cuò)誤的放在S1和S2之間。

如果是形式2,維護(hù)者就不可能犯這種錯(cuò)誤:

Message msg = getMessage();

好,現(xiàn)在我們來(lái)看性能,形式2真的相對(duì)形式1性能更差嗎?對(duì)于下面的代碼:

#include <stdio.h>
?
struct Message
{
? ? Message()
? ? {?
? ? ? ? printf("Message::Message() is called\n");?
? ? }
? ? Message(const Message &)
? ? {
? ? ? ? printf("Message::Message(const Message &msg) is called\n");
? ? }
? ? Message& operator=(const Message &)
? ? {
? ? ? ? printf("Message::operator=(const Message &) is called\n");
? ? }
? ? ~Message()
? ? {
? ? ? ? printf("Message::~Message() is called\n");
? ? }
? ? int a;
? ? int b;
? ? int c;
? ? int d;
? ? int e;
? ? int f;
};
?
Message getMessage()
{
? ? Message result;
? ? result.a = 0x11111111;
?
? ? return result;
}
?
int main()
{
? ? Message msg = getMessage();
? ? return 0;
}

你認(rèn)為運(yùn)行時(shí)會(huì)輸出什么呢?是不是這樣:

Message::Message() is called
Message::Message(const Message &msg) is called
Message::~Message() is called
Message::~Message() is called

并沒有像預(yù)期的輸出那樣。

如果使用MSVC2017編譯,且關(guān)閉優(yōu)化(/Od),確實(shí)可以得到預(yù)期輸入,但是一旦打開優(yōu)化(/O2),輸出就和GCC的一樣了。

我們看看實(shí)際上生成了什么代碼(使用GCC編譯):

(gdb) disassemble main
Dump of assembler code for function main():
? ?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 ?0x72a <getMessage()>
? ?0x000000000000079a <+36>:?? ?mov ? ?$0x0,%ebx
? ?0x000000000000079f <+41>:?? ?lea ? ?-0x30(%rbp),%rax
? ?0x00000000000007a3 <+45>:?? ?mov ? ?%rax,%rdi
? ?0x00000000000007a6 <+48>:?? ?callq ?0x7e4 <Message::~Message()>
? ?0x00000000000007ab <+53>:?? ?mov ? ?%ebx,%eax
? ?0x00000000000007ad <+55>:?? ?mov ? ?-0x18(%rbp),%rdx
? ?0x00000000000007b1 <+59>:?? ?xor ? ?%fs:0x28,%rdx
? ?0x00000000000007ba <+68>:?? ?je ? ? 0x7c1 <main()+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 ??
End of assembler dump.
(gdb) disassemble getMessage?
Dump of assembler code for function getMessage():
? ?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 ?0x7c8 <Message::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 ? ? 0x774 <getMessage()+74>
? ?0x000000000000076f <+69>:?? ?callq ?0x5f0 <__stack_chk_fail@plt>
? ?0x0000000000000774 <+74>:?? ?leaveq?
? ?0x0000000000000775 <+75>:?? ?retq ??
End of assembler dump.

可以看出來(lái),在getMessage函數(shù)中構(gòu)造的對(duì)象實(shí)際上位于main函數(shù)的棧幀上,并沒有額外構(gòu)造一個(gè)Message對(duì)象。這是因?yàn)殚_啟了所謂的返回值優(yōu)化(RVO,Return Value Optimization)的緣故。你想得到的效果編譯器已經(jīng)自動(dòng)幫你完成了,你不必再犧牲什么。

RVO

對(duì)于我們這些用戶來(lái)說(shuō),RVO并不是什么特別復(fù)雜的機(jī)制,主流的GCC和MSVC均支持,也沒什么特別需要注意的地方。它存在的目的是優(yōu)化掉不必要的拷貝復(fù)制函數(shù)的調(diào)用,即使拷貝復(fù)制函數(shù)有什么副作用,例如上面代碼中的打印語(yǔ)句,這可能是唯一需要注意的地方了。從上面的匯編代碼中可以看出來(lái),在GCC中,其基本手段是直接將返回的對(duì)象構(gòu)造在調(diào)用者棧幀上,這樣調(diào)用者就可以直接訪問這個(gè)對(duì)象而不必復(fù)制。

RVO是有限制條件的,在某些情況下無(wú)法進(jìn)行優(yōu)化,在一篇關(guān)于MSVC2005的RVO技術(shù)的文章中,提到了3點(diǎn)導(dǎo)致無(wú)法優(yōu)化的情況:

1. 函數(shù)拋異常

關(guān)于這點(diǎn),我是有疑問的。文章中說(shuō)如果函數(shù)拋異常,開不開RVO結(jié)果都一樣。如果函數(shù)拋異常,無(wú)法正常的返回,我當(dāng)然不會(huì)要求編譯器去做RVO了。

2. 函數(shù)可能返回具有不同變量名的對(duì)象

Message getMessage_NoRVO1(int in)
{
? ? Message msg1;
? ? msg1.a = 1;
?
? ? Message msg2;
? ? msg2.a = 2;
?
? ? if (in % 2)
? ? {
? ? ? ? return msg1;
? ? }
? ? else
? ? {
? ? ? ? return msg2;
? ? }
}

經(jīng)過(guò)驗(yàn)證,在GCC上確實(shí)也是這樣的,拷貝構(gòu)造函數(shù)被調(diào)用了。但這種情況在很多時(shí)候應(yīng)該都是可以通過(guò)重構(gòu)避免的。

Message::Message() is called
Message::Message() is called
Message::Message(const Message &msg) is called
Message::~Message() is called
Message::~Message() is called
Message::~Message() is called

3. 函數(shù)有多個(gè)出口

Message getMessage_NoRVO2(int in)
{
? ? Message msg;
? ? if (in % 2)
? ? {
? ? ? ? return msg;
? ? }
? ? msg.a = 1;
? ? return msg;
}

這個(gè)在GCC上驗(yàn)證發(fā)現(xiàn)RVO仍然生效,查看匯編發(fā)現(xiàn)只有一個(gè)retq指令,多個(gè)出口被優(yōu)化成一個(gè)了。

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

相關(guān)文章

  • C++設(shè)計(jì)模式之迭代器模式

    C++設(shè)計(jì)模式之迭代器模式

    這篇文章主要介紹了C++設(shè)計(jì)模式之迭代器模式,本文講解了什么是迭代器模式、迭代器模式的代碼實(shí)例等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • C++ Qt開發(fā)之PushButton按鈕組件的使用詳解

    C++ Qt開發(fā)之PushButton按鈕組件的使用詳解

    Qt 是一個(gè)跨平臺(tái)C++圖形界面開發(fā)庫(kù),利用Qt可以快速開發(fā)跨平臺(tái)窗體應(yīng)用程序,本文將重點(diǎn)介紹QPushButton按鈕組件的常用方法及靈活運(yùn)用,感興趣的小伙伴可以學(xué)習(xí)一下
    2023-12-12
  • C++元編程語(yǔ)言初步入門詳解

    C++元編程語(yǔ)言初步入門詳解

    這篇文章主要為大家介紹了C++元編程語(yǔ)言初步入門的詳解示例,文中包含詳細(xì)的基本概念及運(yùn)用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-10-10
  • 詳解C++之類和對(duì)象(2)

    詳解C++之類和對(duì)象(2)

    類是創(chuàng)建對(duì)象的模板,一個(gè)類可以創(chuàng)建多個(gè)對(duì)象,每個(gè)對(duì)象都是類類型的一個(gè)變量;創(chuàng)建對(duì)象的過(guò)程也叫類的實(shí)例化。每個(gè)對(duì)象都是類的一個(gè)具體實(shí)例(Instance),擁有類的成員變量和成員函數(shù)
    2021-11-11
  • Qt使用OpenGL實(shí)現(xiàn)繪制3D圖形

    Qt使用OpenGL實(shí)現(xiàn)繪制3D圖形

    OpenGL是一個(gè)跨平臺(tái)的、用來(lái)渲染3D圖形的標(biāo)準(zhǔn)API,Qt對(duì)OpenGL提供了強(qiáng)大的支持,所以本文就來(lái)和大家介紹一下Qt如何使用OpenGL實(shí)現(xiàn)繪制3D圖形吧
    2023-10-10
  • 利用c++編寫簡(jiǎn)易版2048小游戲

    利用c++編寫簡(jiǎn)易版2048小游戲

    這篇文章主要介紹了如何讓利用c++編寫簡(jiǎn)易版的2048小游戲,感興趣的小伙伴請(qǐng)參考下面文章的具體內(nèi)容
    2021-09-09
  • c語(yǔ)言中十進(jìn)制轉(zhuǎn)二進(jìn)制顯示小工具的實(shí)現(xiàn)代碼

    c語(yǔ)言中十進(jìn)制轉(zhuǎn)二進(jìn)制顯示小工具的實(shí)現(xiàn)代碼

    本篇文章是對(duì)c語(yǔ)言中十進(jìn)制轉(zhuǎn)二進(jìn)制顯示小工具的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析的介紹,需要的朋友參考下
    2013-05-05
  • 基于C語(yǔ)言的庫(kù)封裝發(fā)布技術(shù)詳解

    基于C語(yǔ)言的庫(kù)封裝發(fā)布技術(shù)詳解

    在編程的過(guò)程中,使用已經(jīng)封裝好的庫(kù)函數(shù)是十分方便的,也是十分高效的,這篇文章主要給大家介紹了關(guān)于C語(yǔ)言庫(kù)的封裝和使用的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • C語(yǔ)言結(jié)構(gòu)體的一些理解

    C語(yǔ)言結(jié)構(gòu)體的一些理解

    這篇文章主要給大家介紹了關(guān)于C語(yǔ)言結(jié)構(gòu)體的一些理解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • C++使用easyX庫(kù)實(shí)現(xiàn)三星環(huán)繞效果流程詳解

    C++使用easyX庫(kù)實(shí)現(xiàn)三星環(huán)繞效果流程詳解

    EasyX是針對(duì)C/C++的圖形庫(kù),可以幫助使用C/C++語(yǔ)言的程序員快速上手圖形和游戲編程。這篇文章主要介紹了C++使用easyX庫(kù)實(shí)現(xiàn)三星環(huán)繞效果,需要的可以參考一下
    2022-10-10

最新評(píng)論

巨鹿县| 稷山县| 万安县| 甘南县| 远安县| 拜城县| 云林县| 中方县| 湘阴县| 泰顺县| 兴海县| 葫芦岛市| 河津市| 眉山市| 龙门县| 略阳县| 龙口市| 大姚县| 喀喇沁旗| 鹤山市| 梁河县| 乌鲁木齐县| 美姑县| 梨树县| 绥化市| 当涂县| 防城港市| 视频| 藁城市| 榆社县| 卓尼县| 儋州市| 松滋市| 黄冈市| 南平市| 南召县| 平定县| 汽车| 吴江市| 铜山县| 若羌县|