爲何C ++程序員應儘可能減小對「新」的使用?

在使用std :: list <std :: string>時偶然發現了std :: string的內存溢出問題內存泄漏其中一條評論說: 程序員

中止使用new這麼多。 我看不到您在任何地方使用新產品的任何緣由。 您能夠在C ++中按值建立對象,這是使用該語言的巨大優點之一。 您沒必要在堆上分配全部內容。 不要像Java程序員那樣思考。 編程

我不肯定他的意思。 爲何要在C ++中儘量頻繁地經過值建立對象,它在內部有什麼不一樣? 我是否誤解了答案? 多線程


#1樓

C ++ 17以前的版本:

由於即便將結果包裝在智能指針中,它也容易發生細微的泄漏。

考慮一個「謹慎」的用戶,他記得將對象包裝在智能指針中: 函數

foo(shared_ptr<T1>(new T1()), shared_ptr<T2>(new T2()));

該代碼很危險,由於不能保證 T1T2 以前構造了shared_ptr 。 所以,若是new T1()new T2()的一個在另外一個成功以後失敗,則第一個對象將被泄漏,由於不存在用於破壞和從新分配它的shared_ptr工具

解決方案:使用make_shared編碼

後C ++ 17:

這再也不是問題:C ++ 17對這些操做的順序施加了約束,在這種狀況下,確保對 new()每次調用都必須當即跟隨着相應智能指針的構造,而在其中沒有其餘操做之間。 這意味着,在調用第二個 new() ,能夠確保第一個對象已經包裝在其智能指針中,從而防止在引起異常的狀況下發生任何泄漏。 spa

\n

Barry 在另外一個答案中提供了對C ++ 17引入的新評估順序的更詳細說明。 操作系統

感謝@Remy Lebeau指出,這在C ++ 17下仍然是一個問題(儘管更少): shared_ptr構造函數可能沒法分配其控制塊並拋出,在這種狀況下,傳遞給它的指針不會被刪除。 .net

解決方案:使用make_shared線程


#2樓

我發現錯過了一些儘量少的新事物的重要緣由:

new運算符的執行時間不肯定

調用new可能會或可能不會致使操做系統向您的進程分配新的物理頁面,若是您常常這樣作,可能會很慢。 或者它可能已經準備好合適的存儲位置,咱們不知道。 若是你的程序須要有一致的和可預測的執行時間(如在一個實時系統或遊戲/物理模擬),你須要避免new在你的時間臨界循環。

運算符new是一個隱式線程同步

是的,您據說過,您的操做系統須要確保頁面表是一致的,所以調用new將致使您的線程獲取隱式互斥鎖。 若是您一直從許多線程中調用new ,那麼您其實是在對線程進行序列化(我已經用32個CPU進行了此操做,每一個CPU都按下new以得到每一個數百字節的數據,哎呀!這是調試的皇家皮塔餅)

諸如慢,碎片,易於出錯等之類的其餘問題已經在其餘答案中說起。


#3樓

new是新的goto

回想一下爲何goto如此受到譴責:儘管goto是功能強大的底層控制流的工具,但人們常常以沒必要要的複雜方式使用它,這使得代碼難以遵循。 此外,最有用和最容易閱讀的模式是在結構化的編程語句中編碼的(例如forwhile ); 最終的結果是,使用goto做爲適當方法的代碼不多見,若是您很想編寫goto ,則可能作得很差(除非您真的知道本身在作什麼)。

new與此相似-它一般用於使事情變得沒必要要地複雜和難以閱讀,而且能夠編碼的最有用的用法模式已編碼爲各類類。 此外,若是您須要使用尚沒有標準類的任何新用法模式,則能夠編寫本身的對它們進行編碼的類!

我什至認爲new是比goto 更糟糕的 ,由於須要將newdelete語句配對。

goto同樣,若是您曾經認爲須要使用new ,則可能作得很差-尤爲是在類的實現以外,這樣作的目的是封裝全部您須要執行的動態分配。


#4樓

還有一個以上全部正確答案的要點,這取決於您正在執行哪一種編程。 例如,在Windows中開發內核->堆棧受到嚴格限制,您可能沒法像在用戶模式下那樣出現頁面錯誤。

在這樣的環境中,新的或相似C的API調用是首選的,甚至是必需的。

固然,這僅僅是規則的例外。


#5樓

使用new時,對象將分配給堆。 一般在預期擴展時使用。 當您聲明諸如

Class var;

它放在堆棧上。

您將始終必須使用new對放置在堆上的對象調用destroy。 這爲內存泄漏打開了可能。 放置在堆棧上的對象不容易發生內存泄漏!

相關文章
相關標籤/搜索