Modern C++ - RAII & 如何保證記憶體安全

560天前 · 程式設計 · c++ · 1117次阅读

Modern C++ - RAII

RAII (Resource Acquisition Is Initialization) 資源取得即初始化,是現代C++的核心思想之一。
RAII保證了任何會訪問該對象的函式中,資源的可用性
同時它也保證了當對象的生命週期結束時,資源的釋放
透過RAII,可以保證程序的記憶體安全異常安全等。

RAII的基本原理

RAII的基本原理是透過作用域來界定對象的生存期,當退出作用域時,意味着對象的生存期結束,對象將被銷毀 (調用它的解構函式)。

根據這一特性,我們就可以在構造函式初始化資源,並在解構函式釋放資源來實現RAII自動管理資源的目的。

記憶體&異常安全

RAII實現的對象通常是記憶體安全異常安全的。
異常發生時,堆棧上的對象會透過解構函式銷毀。因此即便函式提前退出,也能夠保證資源的釋放

tips:C++標準保證了delete 空指標(nullptr) 的安全。

實現RAII

這是一個簡單的範例:

struct Test{
    int * data;
    Test(int val) {
        data = new int(val);// 申請堆上的記憶體
    }
    ~Test() {
        delete data;// 釋放記憶體
    }
};

int main(){//這是一個作用域,其範圍是該函式的花括號結束。

    Test t(1);//在這裏,資源被申請與初始化

    {//這是一個作用域,其範圍是該花括號語句結束。
        Test t2(2);
    }//作用域結束, 對象t2會在這裏調用解構函式,指標data將會被釋放。

}//作用域結束,對象t會在這裏釋放。

基於RAII設計的標準庫容器有: stringvectorunique_ptrshared_ptr等。

tpis: 擁有成員函式open/closelock/unlockinit/destroy...的類,是典型的非RAII例子。

當然,並非所有資源都適合使用RAII,但合理使用RAII不僅可以保證記憶體安全異常安全,同時也減少了手動資源管理的代碼量,使代碼更簡潔與易維護

移動語義&RAII

移動語義是C++中,允許將一個資源⌈轉移⌋給另一個資源的的方法。
它透過 右值引用(T&&) 和 move方法實現。

拷貝需要完整複製資源,並且原對象和新對象彼此獨立。
而移動則是轉移資源,避免了拷貝的開銷,對象間產生的冗餘。

在RAII中,移動語義也包括生命週期轉移所有權轉移的含義。

struct Test{
    int * data;
    Test(){};
    Test(int val) { data = new int(val); }
    ~Test() { delete data; }

    Test(const Test& other) { //拷貝構造函式
        data = new int(*other.data); // 深拷貝
    }
    Test(Test && other) noexcept : data(other.data) { // 移動構造函式
        other.data = nullptr; //實現通常建議將原對象的資源置空,避免資源重複釋放。
    }
};

int main() {

    Test t(1);  // t.data的記憶體位址是 0x01。
    Test t2 = t; // 拷貝,分配新記憶體,複製數據。現在同時存在0x01和0x02兩份數據。
    Test t3 = std::move(t2); //移動,轉移記憶體所有權,t2現在是空的。

    //這就完成了所有權的轉移,現在t3擁有了0x02的所有權。且沒有產生任何開銷。
    //移動本質上就是將t2.data的指標所有權轉移給了t3.data
    
    //生命週期如何轉移
    Test t4;
    {
        Test t5(2); //記憶體位址0x03,這個變數的生命週期本應在退出作用域時結束。
        t4 = std::move(t5); //現在生命週期被綁定到了t4的生命週期。
    }
}// t4退出作用域,0x03此時才被釋放。
tips: 需要注意,移動語義是實現定義的,這只是一個規範/約定。 也就是說,實際上它也可以什麼也不做,或是直接進行拷貝,這要看移動構造函式是如何編寫的。

移動後,原對象的狀態爲未定義(Undefined behavior)但可解構,其不應再被使用 (即便它可能有效)。
未定義行爲表示任何可能,它可能可以按照你的預期運行,可能是一個無用值,或是引發程序崩潰。
不少初學者難以理解的問題,都是因爲使用到了未定義行爲

完整C++移動語義內容:

小結

RAII實現了C++中的自動資源管理,並保證了記憶體安全異常安全
同時移動語義可以避免資源間轉移的額外開銷
它們都是現代C++ 的核心思想與特性之一。


參閱:

  1. https://en.cppreference.com/w/cpp/language/raii
👍 0

modern c++ raii c++ move

最后修改于94天前

评论

贴吧 狗头 原神 小黄脸
收起

贴吧

  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡

狗头

  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头

原神

  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神

小黄脸

  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸

目录

avatar

Hina

曇花一現

20

文章

96

评论

8

分类

初见

隨筆-視窗接口設計-Qt

370天前

OwO

33

網站正在更新中...
站點正在更新功能與樣式,如有樣式錯誤,請嘗試刷新緩存