共用方式為


物件存留期和資源管理 (現代的 C++)

不同於受管理的語言,C++ 不會有記憶體回收 (GC),以程式執行時,會自動釋放否-較長的時間使用過的記憶體資源。在 C++ 中,資源管理是直接與物件存留期。本文件說明在 C++ 物件存留期,以及如何管理它所影響的因素。

C + + 不會有 GC,主要是因為它並不會處理非記憶體資源。只有決定性的解構函式,例如那些在 C++ 可以同樣處理記憶體和非記憶體資源。GC 還有其他問題,就像在記憶體和 CPU 的負荷和位置較高的額外負荷。但是,universality 是無法透過聰明的最佳化安全防護功能減輕根本的問題。

概念

重要的是,在 [物件存留期管理是封裝 — 無論使用物件並不必知道該物件的資源所擁有的單位,或如何移除它們,或甚至是否其擁有的任何資源根本。它必須終結物件。C + + 的核心語言為了確保會終結物件在正確的時候,也就是當區塊已結束,以相反順序建構。當物件正要終結時,便會毀損其基底和成員以特定順序。語言自動物件終結它們,除非您完成這些特殊工作堆集配置] 或 [新的位置。比方說, 智慧型指標就像unique_ptr和shared_ptr,而標準樣板程式庫 (STL) 的容器要vector,封裝new/delete和new[]/delete[]中的物件,其中具有解構函式。這就是為什麼它是使用智慧型指標和 STL 容器這麼重要。

另一個重要的概念,在存留期管理: 解構函式。解構函式會將封裝資源釋放。(最常使用的助憶鍵是 RRID,資源釋放已損毀)。資源是您從 「 系統 」 取得,也不必稍後提供。記憶體的最常見的資源,但也有檔案、 通訊端、 材質和其他非記憶體資源。「擁有"資源,表示您需要,但您也可以將其與它完成時釋放時您可以使用它。當物件正要終結時,其解構函式會釋放它所擁有的資源。

最後一個概念是 DAG (導向非循環圖形)。在程式中的擁有權結構形成 DAG。物件不能擁有本身 — 這不是只有不可能,但也原本就是沒有意義。但兩個物件都可以共用的第三個物件擁有權。連結的幾種可能會在 DAG,就像這樣: a 是 b 的成員 (B 擁有 A) 時,c 的存放區vector<D> (C 擁有每個 d 的項目),e 的儲存區shared_ptr<F> (E 共用擁有權的 f,可能是與其他物件),這樣的權利。只要有任何循環,且在 DAG 中的每個連結會以物件的解構函式 (而非原始的指標、 控制代碼或其他機制),則資源遺漏是不可能的因為該語言不允許。在不再需要,而不需執行記憶體回收行程之後,立即釋放資源。追蹤的存留期是用於堆疊範圍、 基底、 成員和相關的情況下,釋放額外負荷時間和金錢的shared_ptr。

Hh438473.collapse_all(zh-tw,VS.110).gif堆集為基礎的存留期

對於堆積物件存留期,使用智慧型指標。使用shared_ptr和make_shared做為預設指標並配置器。使用weak_ptr中斷循環、 快取,並觀察到的物件,而不會影響或假設其存留時間相關的任何項目。

void func() {

auto p = make_shared<widget>(); // no leak, and exception safe
...
p->draw(); 

} // no delete required, out-of-scope triggers smart pointer destructor

使用unique_ptr唯一的擁有權,例如,在 pimpl 慣用語。(請參閱Pimpl 的編譯時期封裝 (現代的 C++))。請unique_ptr全部為明確的主要目標new的運算式。

unique_ptr<widget> p(new widget());

您可以使用原始指標為非擁有權及觀察。非主控的指標可能會 dangle,但它不能遺漏。

class node {
  ...
  vector<unique_ptr<node>> children; // node owns children
  node* parent; // node observes parent, which is not a concern
  ...
};
node::node() : parent(...) { children.emplace_back(new node(...) ); }

效能最佳化必要時,您可能必須使用 well-encapsulated 擁有和明確的呼叫,若要刪除的指標。當您實作您自己的低層級的資料結構時,就會是一個例子。

Hh438473.collapse_all(zh-tw,VS.110).gif以堆疊為主的存留期

現代的 C++ 中堆疊為基礎的範圍 是功能強大的方式來撰寫穩定的程式碼,因為它結合自動 堆疊存留期 和 資料成員的存留期以高效率 — 追蹤的存留期是基本上是免費的額外負荷。堆積物件存留期需要仔細的手動管理,且只能由資源遺漏和無效率,來源,特別是當您正在使用未經處理的指標。請考慮下列程式碼,示範了以堆疊為主的領域:

class widget {
private:
  gadget g;   // lifetime automatically tied to enclosing object
public:
  void draw();
};

void functionUsingWidget () {
  widget w;   // lifetime automatically tied to enclosing scope
              // constructs w, including the w.g gadget member
  …
  w.draw();
  …
} // automatic destruction and deallocation for w and w.g
  // automatic exception safety, 
  // as if "finally { w.dispose(); w.g.dispose(); }"

盡量不要使用靜態的存留期 (全域靜態,函式的本機靜態) 因為可能會造成問題。全域物件的建構函式擲回例外狀況時,會發生什麼事?一般而言,應用程式錯誤的方式,可能會很難偵錯。建構的順序是有問題的靜態的存留期的物件,而非並行存取之。不只是有問題,物件建構解構順序可能很複雜,尤其是在多型涉及的地方。即使您的物件或變數不是多型,而且不會有複雜建構/解構的順序,仍有安全執行緒的並行處理的問題。多執行緒應用程式安全地不能修改靜態物件中的資料,而不需執行緒區域儲存區、 資源鎖定和其他特殊的預防措施。

請參閱

其他資源

歡迎回到 C++ (現代的 C++)

C + + 語言參考

標準 C++ 程式庫參考