首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

More Effective C++:建構函數中的資源洩漏防範與 auto_ptr 解決方案

     編輯:關於C++

問題場景:多媒體通訊錄的資源管理

設計一個通訊錄類別 BookEntry,除基本文字資料外,還包含圖片 (Image*) 與音訊 (AudioClip*) 指標。建構函數根據檔案名決定是否 new 這兩個資源,解構函數負責 delete。看似完美,實則暗藏異常安全陷阱。

異常拋出時的資源洩漏

BookEntry::BookEntry(...) : theImage(0), theAudioClip(0) {
    if (imageFileName != "") theImage = new Image(imageFileName);
    if (audioClipFileName != "") theAudioClip = new AudioClip(audioClipFileName); // 可能拋出 bad_alloc 或 AudioClip 建構例外
}

若第二個 new 拋出例外,theImage 已指向有效物件,但 BookEntry 建構未完成,C++ 不會呼叫解構函式,導致 Image 物件洩漏。C++ 規定:只有完全建構完成的物件才會被解構,避免在解構函式中需追蹤建構進度的額外開銷。

嘗試 1:建構函數內 try-catch 手動清理

BookEntry::BookEntry(...) {
    try {
        if (imageFileName != "") theImage = new Image(imageFileName);
        if (audioClipFileName != "") theAudioClip = new AudioClip(audioClipFileName);
    } catch (...) {
        delete theImage; delete theAudioClip; // 清理已配置資源
        throw; // 重新拋出
    }
}

可行但程式碼重複(解構函式也要做同樣清理),應提煉為私有 cleanup()。

嘗試 2:成員初始化列表與 const 指標的困境

若指標宣告為 Image* const theImage,必須在成員初始化列表初始化:

BookEntry::BookEntry(...) 
: theImage(imageFileName != "" ? new Image(imageFileName) : 0),
  theAudioClip(audioClipFileName != "" ? new AudioClip(audioClipFileName) : 0) {}

初始化列表不允許 try-catch 語句(只能含表達式),若 theAudioClip 初始化拋出例外,theImage 又洩漏了。

嘗試 3:委託私有輔助函數處理例外

Image* BookEntry::initImage(const string& f) { 
    return f != "" ? new Image(f) : 0; // 第一個初始化,失敗無洩漏 
}
AudioClip* BookEntry::initAudioClip(const string& f) { // 第二個初始化
    try { return f != "" ? new AudioClip(f) : 0; }
    catch (...) { delete theImage; throw; } // 必須清理先前資源
}

雖解決洩漏,但建構邏輯分散在多個函數,維護困難。

最佳解決方案:使用 auto_ptr (現代 C++ 應用 unique_ptr)

將原始指標改為智慧指標 std::auto_ptr(C++11 後建議 std::unique_ptr),利用 RAII 機制:

class BookEntry {
    // ...
    const std::auto_ptr<Image> theImage;       // 自動管理生命週期
    const std::auto_ptr<AudioClip> theAudioClip;
public:
    BookEntry(...) 
    : theImage(imageFileName != "" ? new Image(imageFileName) : 0),
      theAudioClip(audioClipFileName != "" ? new AudioClip(audioClipFileName) : 0) {}
    ~BookEntry() {} // 空函式,auto_ptr 自動釋放
};

優勢:

  • 異常安全: 若 theAudioClip 初始化失敗,theImage (已完全建構的 auto_ptr) 會自動被解構釋放資源。
  • 簡化解構: 無需手寫 delete,甚至可省略解構函式。
  • 支援 const 指標: 成員初始化列表自然初始化 const auto_ptr。
  • 程式碼集中: 建構邏輯保持在建構函數內,易讀易維護。

總結

建構函數中的資源管理是異常安全的核心挑戰。以 RAII 物件(如 auto_ptr/unique_ptr)取代原始指標,能將資源釋放責任轉移給編譯器自動管理的物件生命週期,徹底解決建構失敗時的洩漏問題,同時簡化程式碼結構。這是現代 C++ 資源管理的基石模式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved