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

Effective C++ 條款 13:以對象管理資源(RAII)

     編輯:關於C++

條款 13:以對象管理資源

核心思想:將資源封裝在對象內,利用 C++ 析構函數自動調用機制 確保資源釋放。這就是著名的 RAII(Resource Acquisition Is Initialization,資源獲取即初始化) 慣用語。

問題背景

許多資源(動態內存、文件句柄、鎖、Socket、數據庫連接)在堆上分配,用於單一作用域/函數。它們必須在控制流離開該作用域時被釋放。手動管理極易出錯:

  • 忘記釋放 → 資源洩漏
  • 提前返回/異常拋出 → 釋放代碼被跳過
  • 重複釋放 → 未定義行為

標準庫方案:auto_ptr (C++98) / unique_ptr (C++11)

auto_ptr 示例

class Investment { ... };
Investment* createInvestment(); // 返回動態分配對象,調用者負責刪除

void f() {
    std::auto_ptr<Investment> pInv(createInvestment());
    // 使用 pInv...
} // pInv 析構時自動 delete 所指對象,無洩漏風險

auto_ptr 關鍵特性(及缺陷)

  • 構造時接管所有權
  • 析構時 delete 對象
  • 拷貝/賦值會轉移所有權:源 auto_ptr 置空,目標接管
  • 缺陷:std::vector<auto_ptr> 非法、std::sort 失效、不可用於數組

更優方案:引用計數智能指針

std::tr1::shared_ptr (C++11 std::shared_ptr) 共享所有權,最後一個銷毀時釋放:

void f() {
    std::tr1::shared_ptr<Investment> pInv1(createInvestment());
    std::tr1::shared_ptr<Investment> pInv2(pInv1); // 共享
    pInv1 = pInv2; // 賦值共享
    // 離開作用域時,最後一個 shared_ptr 釋放對象
}

兩個關鍵設計原則

  1. 資源獲取即初始化:在構造函數中獲取資源(或接管裸指針)
  2. 析構函數釋放資源:析構函數自動調用,無視返回路徑(正常/異常/提前 return)

常用 RAII 類

記住

為防止資源洩漏,請使用 RAII 對象:構造函數獲取資源,析構函數釋放資源。
兩個常用 RAII 類:std::tr1::shared_ptr 和 auto_ptr。前者優於後者,因為拷貝行為直觀(共享而非轉移)。若選 auto_ptr,拷貝會使源對象置空。

現代 C++ 最佳實踐 (C++11/14/17/20)

  • 獨占所有權:std::unique_ptr (配合 std::make_unique C++14)
  • 共享所有權:std::shared_ptr (配合 std::make_shared)
  • 弱引用/觀察者:std::weak_ptr
  • 鎖管理:std::lock_guard (簡單)、std::unique_lock (靈活)
  • 自定義資源:封裝裸指針/句柄到 RAII 類,指定自定義刪除器
// 自定義 RAII 範例:管理 C 風格資源
class FileGuard {
    FILE* fp_;
public:
    explicit FileGuard(const char* path, const char* mode) : fp_(fopen(path, mode)) {
        if (!fp_) throw std::runtime_error("fopen failed");
    }
    ~FileGuard() { if (fp_) fclose(fp_); }
    // 禁止拷貝,允許移動
    FileGuard(const FileGuard&) = delete;
    FileGuard& operator=(const FileGuard&) = delete;
    FileGuard(FileGuard&& other) noexcept : fp_(other.fp_) { other.fp_ = nullptr; }
    FileGuard& operator=(FileGuard&& other) noexcept {
        if (this != &other) { if (fp_) fclose(fp_); fp_ = other.fp_; other.fp_ = nullptr; }
        return *this;
    }
    FILE* get() const { return fp_; }
};

// 使用
void writeLog() {
    FileGuard fg("app.log", "a");
    fprintf(fg.get(), "Log entry\n");
} // 自動 fclose
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
資源類型RAII 封裝標準庫
動態內存unique_ptr / shared_ptr<memory>
互斥鎖lock_guard / unique_lock<mutex>
文件fstream / FILE* 封裝<fstream>
Socket自定義 SocketGuard-
數據庫連接自定義 ConnectionGuard-