www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > > 充電吧
[導(dǎo)讀]讓我們從std::make_unique和std::make_shared之間的比較開始講起吧。std::make_shared是C++11的一部分,可惜的是,std::make_unique不是,它

讓我們從std::make_unique和std::make_shared之間的比較開始講起吧。std::make_shared是C++11的一部分,可惜的是,std::make_unique不是,它在C++14才納入標(biāo)準(zhǔn)庫。如果你使用的是C++11,不用憂傷,因為std::make_unique的簡單版本很容易寫出來,不信你看:


templatestd::unique_ptrmake_unique(Ts&&...?params)
{
????return?std::unique_ptr(new?T(std::forward(params)...));
}

就像你看到的那樣,make_unique只是把參數(shù)完美轉(zhuǎn)發(fā)給要創(chuàng)建對象的構(gòu)造函數(shù),再從new出來的原生指針構(gòu)造std::unique_ptr,最后返回創(chuàng)建的std::unique_ptr。這種形式的函數(shù)不支持?jǐn)?shù)組和自定義刪除器,但它說明了只要一點點工作,你就可以創(chuàng)造你需要的make_unique了。你要記住不要把你自己的版本放入命名空間std,因為當(dāng)你提升到C++14標(biāo)準(zhǔn)庫實現(xiàn)的時候,你不會想要它和標(biāo)準(zhǔn)庫的版本沖突。

std::make_unique和std::make_shared是三個make函數(shù)中的其中兩個,而make函數(shù)是:把任意集合的參數(shù)完美轉(zhuǎn)發(fā)給動態(tài)分配對象的構(gòu)造函數(shù),然后返回一個指向那對象的智能指針。第三個make函數(shù)是std::allocate_shared,它的行為與std::make_shared類似,除了它第一個參數(shù)是個分配器,指定動態(tài)分配對象的方式。

通過瑣碎比較使用make函數(shù)和不使用make函數(shù)創(chuàng)建智能指針,揭露了使用make函數(shù)更可取的第一個原因??紤]以下:


auto?upw1(std::make_unique());?????//?使用make函數(shù)
std::unique_ptrupw2(new?Widget);??//?不使用make函數(shù)
auto?spw1(std::make_shared());?????//?使用make函數(shù)
std::shared_ptrspw2(new?Widget);??//?不使用make函數(shù)

它們本質(zhì)上的不同是:使用new的版本重復(fù)著需要創(chuàng)建的類型(即出現(xiàn)了兩次Widget),而使用make函數(shù)不需要。重復(fù)出現(xiàn)類型和軟件工程的關(guān)鍵原則產(chǎn)生沖突:應(yīng)該避免代碼重復(fù)。源碼中重復(fù)的代碼會增加編譯時間,導(dǎo)致對象代碼膨脹,并且通常會讓代碼庫更難運行——這經(jīng)常引發(fā)不合邏輯的代碼,而不合邏輯的代碼庫一般會出現(xiàn)bug。除非是寫兩次比寫一個更有效果,不然誰不喜歡少些點代碼嗎?


更偏愛使用make函數(shù)的第二個原因異常安全。假如我們有個函數(shù),根據(jù)優(yōu)先級來處理Widget:


void?processWidget(std::shared_ptrspw,?int?priority);

現(xiàn)在呢,假如我們有個計算優(yōu)先級的函數(shù),


int?computePriority();

然后我們用它和new創(chuàng)建的智能指針作為參數(shù)調(diào)用processWidget:


processWidget(std::shared_ptr(new?Widget),?
??????????????computePriority());????//?可能會資源泄漏

就如注釋所說,這代碼中new出來的Widget可能會泄漏,但是為什么?std::shared_ptr是為了防止資源泄漏而設(shè)計的,當(dāng)最后一個指向資源的std::shared_ptr對象消失,它們指向的資源也會被銷毀。如果每個人無論什么地方都使用std::shared_ptr,C++還有內(nèi)存泄漏這回事嗎?

答案是在編譯期間,源代碼轉(zhuǎn)換為目標(biāo)碼時(*.o文件)。在運行時間,函數(shù)的參數(shù)在函數(shù)運行前必須被求值,所以調(diào)用processWidget時,下面的事請會在processWidget開始前執(zhí)行:

表達式“new Widget”會被求值,即,一個Widget對象必須在堆上被創(chuàng)建。std::shared_ptr的接收原生指針的構(gòu)造函數(shù)一定要執(zhí)行。computePriority一定要運行。

編譯器在生成代碼時不會保證上面的執(zhí)行順序,“new Widget”一定會在std::shared_ptr構(gòu)造函數(shù)之前執(zhí)行,因為構(gòu)造函數(shù)需要new的結(jié)果,但是computePriority可能在它們之前就被調(diào)用了,可能在它們之后,可能在它們之間。所以,編譯器生成代碼的執(zhí)行順序有可能是這樣的:

執(zhí)行“new Widget”。執(zhí)行computePriority。執(zhí)行std::shared_ptr的構(gòu)造函數(shù)。

如果生成的代碼真的是這樣,那么在運行時,computePriority產(chǎn)生了異常,步驟1中動態(tài)分配的Widget就泄漏了,因為它沒有被步驟3中的std::shared_ptr保存。

使用std::make_shared_ptr可以避免這問題。這樣調(diào)用代碼:


processWidget(std::make_shared(),?computePriority())

在運行期間,std::make_shared和computePriority都有可能先被調(diào)用,如果先調(diào)用的是std::make_shared,那么指向動態(tài)分配Widget對象的原生指針會安全地存儲在要返回的std::shared_ptr中,然后再調(diào)用computePriority。如果computePriority產(chǎn)出異常,std::shared_ptr的析構(gòu)函數(shù)就會銷毀持有的Widget。而如果先調(diào)用的是computePriority,并且產(chǎn)生異常,std::make_shared就不會被執(zhí)行,因此沒有動態(tài)分配的Widget對象讓你擔(dān)心。

如果我們把std::shared_ptr和std::make_shared替換成std::unique_ptr和std::make_unique,效果一樣。使用std::make_unique替代new的重要性就像使用std::make_shared那樣:寫異常安全的代碼。


std::make_shared的一個特點(相比于直接使用new)是提高效率。使用std::make_shared允許編譯器生成更小、更快的代碼??紤]當(dāng)我們直接使用new時:


std::shared_ptrspw(new?Widget);

很明顯這代碼涉及一次內(nèi)存分配,不過,它實際上分配兩次。每個std::shared_ptr內(nèi)都含有一個指向控制塊的指針,這控制塊的內(nèi)存是由std::shared_ptr的構(gòu)造函數(shù)分配的,那么直接使用new,需要為Widget分配一次內(nèi)存,還需要為控制塊分配一次內(nèi)存。

如果用std::make_shared呢,


auto?spw?=?std::make_shared();

一次分配就夠了,因為std::make_shared會分配一大塊內(nèi)存來同時持有Widget對象和控制塊。這種優(yōu)化減少了程序的靜態(tài)尺寸,因為代碼只需要調(diào)用一次內(nèi)存分配函數(shù),然后它增加了代碼執(zhí)行的速度,因為只需要分配一次內(nèi)存(說明是分配內(nèi)存這個函數(shù)開銷略大)。而且,使用std::make_shared能避免了一些控制塊的簿記信息,潛在地減少了程序占用的內(nèi)存空間。

std::allocate_shared的性能分析和std::make_shared一樣,所以std::make_shared的性能優(yōu)勢也可以延伸到std::allocate_shared。


比起直接使用new,更偏愛使用make函數(shù),這個爭論是很熱烈的。雖有軟件工程、異常安全、性能優(yōu)勢,不過,本條款的指導(dǎo)方針是更偏愛使用make函數(shù),而不是單獨依賴它們,這是因為在某些狀況下它們不適用。

例如,沒有一個make函數(shù)可以指定自定義刪除器,但是std::unique_ptr和std::shared_ptr都有這樣的構(gòu)造函數(shù)。給定一個Widget的自定義刪除器,


auto?widgetDeleter?=?[](Widget*?pw)?{...}

我們可以直接使用new創(chuàng)建智能指針:


std::unique_ptrupw(new?Widget,?widgetDeleter);

std::shared_ptrspw(new?Widget,?widgetDeleter);

make函數(shù)就做不來這種事情。


make函數(shù)的第二個限制是來源于它們實現(xiàn)的句法細(xì)節(jié)。當(dāng)創(chuàng)建一個對象時,如果該對象的重載構(gòu)造函數(shù)帶有std::initializer_list參數(shù),那么使用大括號創(chuàng)建對象會偏向于使用帶std::initializer_list構(gòu)造,要使用圓括號創(chuàng)建對象才能使用到非std::initializer_list構(gòu)造。make函數(shù)把它們的參數(shù)完美轉(zhuǎn)發(fā)給對象的構(gòu)造函數(shù),那么它們用的是大括號還是圓括號呢?對于某些類型,這問題的答案的不同會導(dǎo)致結(jié)果有很大差異。例如,在這些調(diào)用中,


auto?upv?=?std::make_unique<std::vector>(10,?20);
auto?spv?=?std::make_shared<std::vector>(10,?20);

指針指向的是帶10個元素、每個值為20的std::vector呢,還是指向兩個元素、一個10、一個20的std::vector呢?還是說結(jié)果不能確定嗎?

好消息是結(jié)果是能確定的:上面兩個都創(chuàng)建內(nèi)含10個值為20的std::vector。那意味著在make函數(shù)內(nèi),完美轉(zhuǎn)發(fā)使用的是圓括號,而不是大括號。壞消息是如果你想用大括號初始化來構(gòu)造指向的對象,你只能直接使用new,如果你想使用make函數(shù),就要求完美轉(zhuǎn)發(fā)的能力支持大括號初始化,但是大括號初始化不能被完美轉(zhuǎn)發(fā)。不過也有一種能工作的方法:用auto推斷大括號,從而創(chuàng)建一個std::initializer_list對象,然后把auto變量傳遞給make函數(shù):


//?創(chuàng)建?std::initializer_list
auto?initList?=?{10,?20};

//?使用std::initializer_list構(gòu)造函數(shù)創(chuàng)建std::vector,容器中只有兩個元素
auto?spv?=?std::make_shared<std::vector>(initList);

對于std::unique_ptr,只有兩種情況(自定義刪除器和大括號初始化)會讓它的make函數(shù)出問題。對于std::shared_ptr和它的make函數(shù),就多兩種情況,這兩種情況都是邊緣情況,不過一些開發(fā)者就喜歡住在邊緣,你可能就是他們中第一個。

一些類定義了自己的operator new和operator delete函數(shù),這些函數(shù)的出現(xiàn)暗示著常規(guī)的全局內(nèi)存分配和回收不適合這種類型的對象。通常情況下,設(shè)計這些函數(shù)只有為了精確分配和銷毀對象,例如,Widget對象的operator new和operator delete只有為了精確分配和回收大小為sizeof(Widget)的內(nèi)存塊才會設(shè)計。這兩個函數(shù)不適合std::shared_ptr的自定義分配(借助std::allocate_shared)和回收(借助自定義刪除器),因為std::allocate_shared請求內(nèi)存的大小不是對象的尺寸,而是對象尺寸加上控制塊尺寸。結(jié)果就是,使用make函數(shù)為那些——定義自己版本的operator new和operator delete的——類創(chuàng)建對象是個糟糕的想法。


比起直接使用new,std::make_shared的占用內(nèi)存大小和速度優(yōu)勢來源于:std::shared_ptr的控制塊與它管理的對象放在同一塊內(nèi)存。當(dāng)引用計數(shù)為0時,對象被銷毀(即調(diào)用了析構(gòu)函數(shù)),但是,它使用的內(nèi)存不會釋放,除非控制塊也被銷毀,因為對象和控制塊在同一塊動態(tài)分配的內(nèi)存上。

就像我提起那樣,控制塊上除了引用計數(shù)還有別的薄記信息。引用計數(shù)記錄的是有多少std::shared_ptr指向控制塊,但是控制塊還有第二種引用計數(shù),記錄有多少std::weak_ptr指向控制塊。這種引用計數(shù)稱為weak count。當(dāng)std::weak_ptr檢查它是否過期時(expired),它通過檢查控制塊中的引用計數(shù)(不是weak count)來實現(xiàn)。如果引用計數(shù)為0(即沒有std::shared_ptr指向這個對象,因此被銷毀),std::weak_ptr就過期,否則就沒有過期。

但是,只要有std::weak_ptr指向控制塊(weak count大于0),控制塊就必須繼續(xù)存在,而只要控制塊存在,容納它的內(nèi)存塊也依舊存在。那么,通過make函數(shù)創(chuàng)建對象分配的內(nèi)存,要直到最后一個指向它的std::shared_ptr和std::weak_ptr對象銷毀,才能被回收。

如果對象的類型非常大,并且最后一個std::shared_ptr銷毀和最后一個std::weak_ptr銷毀之間的時間間隔很大,那么是對象銷毀和內(nèi)存被回收之間的會有延遲:


class?ReallyBigType?{?...?};

auto?pBigObj?=??????????????????????????//?借助std::make_shared
???std::make_shared();???//?創(chuàng)建類型非常大的對象
...???????????????//?創(chuàng)建std::shared_ptr和std::weak_ptr指向?qū)ο?
...???????????????//?最后一個std::shared_ptr被銷毀,那仍有std::weak_ptr存在
...???????????????//?在這個期間,之前類型非常大的對象使用的內(nèi)存仍然被占用
...???????????????//?最后一個std::weak被銷毀,控制塊和對象共占的內(nèi)存被釋放

如果直接使用new,ReallyBigType對象的內(nèi)存只要在最后一個std::shared_ptr被銷毀就能被釋放:


class?ReallyBigType?{?...?};

auto?pBigObj?=??????????????????????????//?借助std::make_shared
???std::make_shared();???//?創(chuàng)建類型非常大的對象
...???????????????//?創(chuàng)建std::shared_ptr和std::weak_ptr指向?qū)ο?
...???????????????//?最后一個std::shared_ptr被銷毀,那仍有std::weak_ptr存在
...???????????????//?在這個期間,之前類型非常大的對象使用的內(nèi)存仍然被占用
...???????????????//?最后一個std::weak被銷毀,控制塊和對象共占的內(nèi)存被釋放

當(dāng)你發(fā)現(xiàn)某些情況不能使用或者不適合使用std::make_shared,卻又想要防止容易發(fā)生的異常安全問題。最好的辦法就是確保當(dāng)你直接使用new時,用一條語句執(zhí)行——把new的結(jié)果馬上傳遞給智能指針的構(gòu)造函數(shù),并且該語句就做這一件事。這防止編譯器生成new和std::shared_ptr構(gòu)造之間發(fā)出異常。

作為例子,我們修改之前的異常不安全processWidget,并指定自定義刪除器:


void?processWidget(std::shared_ptrspw,?int?priority);?//?如前
void?cusDel(Widget?*ptr);??????//??自定義刪除器

這里是異常不安全的調(diào)用:


processWidget(???????????//?如前,可能資源泄漏
???std::shared_ptr(new?Widget,?cusDel),
???computePriority()
);

回憶:如果computePriority調(diào)用在“new Widget”之前,std::shared_ptr構(gòu)造之后,然后computePriority產(chǎn)生異常,那么動態(tài)分配的Widget就會泄漏。

這里要使用自定義刪除器,不能使用std::make_shared,所以避免泄漏的方法就是把分配Widget和std::shared_ptr構(gòu)造放在只屬于它們的語句,然后再用std::shared_ptr的結(jié)果調(diào)用processWidget。這是這項技術(shù)的本質(zhì)部分,等下我們可見到,我們可以修改它從而提高性能:


std::shared_ptrspw(new?Widget,?cusDel);
processWidget(spw,?computeWidget);??//?正確,但沒有優(yōu)化,看下面

這代碼是可行的,因為std::shared_ptr得到了原生指針的所有權(quán),盡管構(gòu)造函數(shù)可能發(fā)出異常。在這個例子中,如果spw的構(gòu)造期間拋出異常(例如,由于不能為控制塊動態(tài)分配內(nèi)存),也能保證cusDel被調(diào)用(以“new Widget”的結(jié)果為參數(shù))。

有個小小的性能問題,在異常不安全的調(diào)用中,我們傳給processWidget的是一個右值,


processWidget(
???std::shared_ptr(new?Widget,?cusDel),??//?參數(shù)是右值
???computePriority()
);

但是在異常安全的調(diào)用中,我們傳遞的是個左值:


processWidget(spw,?computePriority());???//?參數(shù)是左值

因為processWidget的std::shared_ptr參數(shù)是值傳遞,從一個右值構(gòu)造使用的是移動,從一個左值構(gòu)造使用的是拷貝。對于std::shared_ptr,這差別挺大的,因為拷貝一個std::shared_ptr需要增加它的引用計數(shù),這是原子操作,而移動操作完全不用操作引用計數(shù)。針對于異常安全代碼想要達到異常不安全代碼的性能水平,我們需要使用std::move來把spw轉(zhuǎn)化為右值:


processWidget(std::move(spw),?computePriority());??//?現(xiàn)在也一樣高效

這是有趣的而且值得知道,但是通常也是不相干的,因為你很少有理由不用make函數(shù),除非你有迫不得已的理由,否則,你應(yīng)該使用make函數(shù)。

總結(jié)

需要記住的3點:

相比于直接使用new,make函數(shù)可以消除代碼重復(fù),提高異常安全,而且std::make_shared和std::allocate_shared生成的代碼更小更快。不適合使用make函數(shù)的場合包括需要指定自定義刪除器和想要傳遞大括號初始值。對于std::shared_ptr,使用make函數(shù)可能是不明智的額外場合包括(1)自定義內(nèi)存管理函數(shù)的類和(2)內(nèi)存緊張的系統(tǒng)中,有非常大的對象,然后std::weak_ptr比std::shared_ptr長壽。

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當(dāng)下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉