物件存留期及資源管理 (RAII)

不同于 Managed 語言,C++ 沒有自動 垃圾收集 ,這是一個內部進程,會在程式執行時釋放堆積記憶體和其他資源。 C++ 程式負責將所有已取得的資源傳回至作業系統。 無法釋放未使用的資源稱為 洩漏 。 在程式結束之前,其他程式無法使用外泄的資源。 特別是記憶體流失是 C 樣式程式設計中 Bug 的常見原因。

新式 C++ 可藉由在堆疊上宣告物件,盡可能避免使用堆積記憶體。 當資源對堆疊而言太大時,它應該 由 物件所 擁有。 當物件初始化時,它會取得其擁有的資源。 然後,物件會負責在其解構函式中釋放資源。 擁有物件本身會在堆疊上宣告。 物件擁有資源 的原則也稱為「資源擷取是初始化」或 RAII。

當資源擁有的堆疊物件超出範圍時,會自動叫用其解構函式。 如此一來,C++ 中的垃圾收集與物件存留期密切相關,而且具決定性。 資源一律會在程式中的已知點發行,您可以控制此資源。 只有像 C++ 中的那些具決定性的解構函式能均衡處理記憶體和非記憶體資源。

下列範例顯示簡單的 物件 w 。 它會在函式範圍的堆疊上宣告,並在函式區塊的結尾終結。 物件 w 沒有 資源 (例如堆積配置的記憶體)。 其唯一的成員 g 本身是在堆疊上宣告,而且只會超出範圍和 w 。 解構函式中 widget 不需要任何特殊程式碼。

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(); }"

在下列範例中, w 擁有記憶體資源,因此其解構函式中必須有程式碼才能刪除記憶體。

class widget
{
private:
    int* data;
public:
    widget(const int size) { data = new int[size]; } // acquire
    ~widget() { delete[] data; } // release
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data member
    w.do_something();

} // automatic destruction and deallocation for w and w.data

自 C++11 起,有更好的方法可以撰寫上述範例:使用標準程式庫的智慧型指標。 智慧型指標會處理其擁有之記憶體的配置和刪除。 使用智慧型指標可免除類別中 widget 明確解構函式的需求。

#include <memory>
class widget
{
private:
    std::unique_ptr<int[]> data;
public:
    widget(const int size) { data = std::make_unique<int[]>(size); }
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data gadget member
    // ...
    w.do_something();
    // ...
} // automatic destruction and deallocation for w and w.data

藉由使用智慧型指標進行記憶體配置,您可以消除記憶體流失的可能性。 此模型適用于其他資源,例如檔案控制代碼或通訊端。 您可以在類別中以類似的方式管理自己的資源。 如需詳細資訊,請參閱 智慧型 指標。

C++ 的設計可確保物件超出範圍時會終結。 也就是說,它們被摧毀,因為區塊被退出,以相反的建設順序。 在物件終結時,它的基底和成員會以特殊的順序終結。 在全域範圍的任何區塊之外宣告的物件可能會導致問題。 如果全域物件的建構函式擲回例外狀況,可能很難進行偵錯。

另請參閱

歡迎回到 C++
C++ 語言參考
C++ 標準程式庫