久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

C 系列 | 每一個(gè)C 程序員都應(yīng)該知道的RAII

 芥子c1yw3tb42g 2025-01-24

C++: RAII是什么——使用對象來管理資源

導(dǎo)讀:RAII是C++中一種管理資源,、避免資源泄漏的慣用法,利用棧對象自動(dòng)銷毀的特點(diǎn)來實(shí)現(xiàn),。本文較為詳細(xì)介紹了RAII的原理,、使用方法和優(yōu)點(diǎn),并且通過實(shí)例講解了RAII在C++ STL中的應(yīng)用,如智能指針和互斥鎖等,,在最后進(jìn)行了編程實(shí)踐,。本文適合對C++編程有一定了解的開發(fā)者閱讀。

1. 什么是RAII

RAII是Resource Acquisition Is Initialization的縮寫,,即“資源獲取即初始化”,。它是C++語言的一種管理資源、避免資源泄漏的慣用法,,利用棧對象自動(dòng)銷毀的特點(diǎn)來實(shí)現(xiàn),,這一概念最早由Bjarne Stroustrup提出。因此,,我們可以通過構(gòu)造函數(shù)獲取資源,,通過析構(gòu)函數(shù)釋放資源。即:

代碼語言:javascript
復(fù)制
Object() {// acquire resource in constructor}~Object() {// release resource in destructor}

RAII總結(jié)如下:

  • 將每一種資源封裝在一個(gè)RAII類中:

    • 所有資源在構(gòu)造函數(shù)中獲取,,例如:分配內(nèi)存,、打開文件、建立數(shù)據(jù)庫連接等,;如果無法完成則在構(gòu)造函數(shù)中拋出異常,;

    • 所有資源在析構(gòu)函數(shù)中釋放,例如:釋放內(nèi)存,、關(guān)閉文件,、銷毀數(shù)據(jù)庫連接等;不應(yīng)該拋出任何異常,。

  • 通過RAII類實(shí)例獲取資源:

    • 具有自動(dòng)生命管理周期或臨時(shí)對象生命周期

    • 其生命周期與第一種綁定,。

2. 為什么要使用RAII

我們知道,在C++中,,通過new運(yùn)算符動(dòng)態(tài)申請內(nèi)存,,例如:

代碼語言:javascript
復(fù)制
Foo* ptr = new Foo(1);// ...delete ptr;

在這段代碼中,new運(yùn)算符在計(jì)算機(jī)內(nèi)存的堆上申請了一塊Foo類型的內(nèi)存,,然后將其地址賦值給存儲(chǔ)在棧上的指針ptr。為了能夠釋放內(nèi)存資源,,我們需要使用完new運(yùn)算符申請的內(nèi)存后,,手動(dòng)調(diào)用delete運(yùn)算符釋放內(nèi)存。

但是,,情況并不總是如此簡單,。

代碼語言:javascript
復(fù)制
Foo* ptr = new Foo(1);f(ptr); // -->① may throw exceptionif(ptr->g()) {// ... --> ② may forget to delete ptrreturn;}// ...delete ptr;

如上面這個(gè)例子,我們可能會(huì)遇到以下幾種情況:

  1. 忘記delete釋放內(nèi)存,。比如釋放原指針指向的內(nèi)存前就改變了指針的指向,。

  2. 程序拋出異常后導(dǎo)致無法delete。比如上面的①處,如果f函數(shù)拋出異常,,沒有機(jī)會(huì)運(yùn)行delete,,從而導(dǎo)致內(nèi)存泄漏。

  3. 需求變更后,,修改了函數(shù),,新增了分支,提前返回,,②處卻沒有delete,;現(xiàn)實(shí)情況代碼復(fù)雜的話可能沒有這么顯而易見。

而通過RAII這樣一種機(jī)制,,我們可以使其自動(dòng)釋放內(nèi)存,。

3. C++ STL中RAII的應(yīng)用

3.1 智能指針

智能指針是RAII的一種實(shí)現(xiàn),它是一種模板類,,用于管理動(dòng)態(tài)分配的對象,。智能指針的主要作用是自動(dòng)釋放內(nèi)存,從而避免內(nèi)存泄漏,。C++11中提供了三種智能指針:unique_ptr,、shared_ptr和weak_ptr。它們的詳細(xì)原理將在之后的文章中介紹,。這里我們以unique_ptr為例,,它的構(gòu)造函數(shù)如下:

代碼語言:javascript
復(fù)制
template< class T, class Deleter = std::default_delete<T> > class unique_ptr;

unique_ptr的析構(gòu)函數(shù)會(huì)自動(dòng)釋放內(nèi)存,因此,,我們可以通過unique_ptr來管理動(dòng)態(tài)分配的內(nèi)存,,從而避免內(nèi)存泄漏。例如:

代碼語言:javascript
復(fù)制
std::unique_ptr<int> ptr = std::make_unique<int>(1); // release memory when ptr is out of scope

3.2 互斥鎖

在多線程編程中,,std::lock_guard, std::unique_lock, std::shared_lock等也利用了RAII的原理,,用于管理互斥鎖。當(dāng)這些類的等對象創(chuàng)建時(shí),,會(huì)自動(dòng)獲取互斥鎖,;當(dāng)對象銷毀時(shí),會(huì)自動(dòng)釋放互斥鎖,。

std::lock_guard的構(gòu)造函數(shù)如下:

代碼語言:javascript
復(fù)制
template< class Mutex > class lock_guard;

std::lock_guard的析構(gòu)函數(shù)會(huì)自動(dòng)釋放互斥鎖,,因此,我們可以通過std::lock_guard來管理互斥鎖,,從而避免忘記釋放互斥鎖,。例如:

代碼語言:javascript
復(fù)制
std::mutex mtx;std::lock_guard<std::mutex> lock(mtx); // unlock when lock is out of scope

不使用RAII的情況下,我們需要手動(dòng)釋放互斥鎖,,如下所示:

代碼語言:javascript
復(fù)制
std::mutex mtx;mtx.lock();// ...mtx.unlock();

3.3 文件操作

std::ifstream, std::ofstream等C++標(biāo)準(zhǔn)庫的IO操作都是RAII的實(shí)現(xiàn),。

3.4 事務(wù)處理

數(shù)據(jù)庫事務(wù)處理中,如果在事務(wù)結(jié)束時(shí)沒有提交或回滾,就會(huì)導(dǎo)致數(shù)據(jù)庫連接一直被占用,,從而導(dǎo)致數(shù)據(jù)庫連接池耗盡,。因此,我們需要在事務(wù)結(jié)束時(shí)自動(dòng)提交或回滾,,從而釋放數(shù)據(jù)庫連接,。這一過程也可以通過RAII來實(shí)現(xiàn)。

3.5 其他

RAII還可以用于管理其他資源,,比如網(wǎng)絡(luò)連接,、線程等。

4. RAII的編程實(shí)踐

基于RAII實(shí)現(xiàn)資源池的自動(dòng)回收機(jī)制:

ResourcePool為資源池類,,可以創(chuàng)建指定數(shù)量的資源,,并提供獲取和釋放資源的接口。

ResourceWrapper為資源包裝類,,用于獲取資源,,并在對象銷毀時(shí)自動(dòng)釋放資源。

Resource為資源類,,用于模擬資源,,通過id來標(biāo)識,其構(gòu)造函數(shù)和析構(gòu)函數(shù)分別用于獲取和釋放資源,。

實(shí)現(xiàn)資源管理類需要注意的一些事項(xiàng):

  • 需要仔細(xì)考慮拷貝構(gòu)造函數(shù)和拷貝賦值運(yùn)算符的實(shí)現(xiàn),,若需拷貝,應(yīng)考慮實(shí)現(xiàn)引用計(jì)數(shù)或?qū)Y源進(jìn)行深拷貝,;若無必要,,最好將其刪除。這里我們使用了=delete進(jìn)行了刪除,;

  • 需提供移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符,,以便于使用std::move(),轉(zhuǎn)移資源的控制權(quán),;

  • 提供獲取原始資源的接口,。

代碼實(shí)現(xiàn)如下:

代碼語言:javascript
復(fù)制
#include <iostream>#include <vector>#include <deque>constexpr int kErrorId = -1;template<typename T>class ResourcePool {public:ResourcePool(int size) {for (int i = 0; i < size; ++i) {pool_.emplace_back(i);}}T getResource() {if (pool_.empty()) {std::cout << 'Resource is not available now.' << std::endl;return T();}T resource = std::move(pool_.front());pool_.pop_front();std::cout<< 'Resource ' << resource.ID() << ' is acquired.' << std::endl;return resource;}void releaseResource(T&& resource) {if (resource.ID() == kErrorId) { return;}std::cout << 'Resource ' << resource.ID() << ' is released.' << std::endl;pool_.emplace_back(std::forward<T>(resource));}private:std::deque<T> pool_;};template<typename T>class ResourceWrapper {public:ResourceWrapper(ResourcePool<T>& pool) : pool_(pool), resource_(pool_.getResource()) { if(resource_.ID() == kErrorId) {throw std::runtime_error('Resource is not available now.'); }}~ResourceWrapper() {pool_.releaseResource(std::move(resource_));}ResourceWrapper(const ResourceWrapper& other) = delete;ResourceWrapper& operator=(const ResourceWrapper& other) = delete;ResourceWrapper(ResourceWrapper&& other) noexcept : pool_(other.pool_), resource_(std::move(other.resource_)) {}ResourceWrapper& operator=(ResourceWrapper&& other) noexcept { pool_ = other.pool_; resource_ = std::move(other.resource_); return *this;}const T& GetRawResource() const noexcept{return resource_;}private:ResourcePool<T>& pool_;T resource_;};class Resource {public:constexpr explicit Resource(int id) : id_(id) { std::cout << 'Resource ' << id_ << ' is created.' << std::endl;}Resource(): id_(kErrorId) {}~Resource() = default;int ID() const {return id_;}// delete copy constructor and copy assignment operatorResource(const Resource& other) = delete;Resource& operator=(const Resource& other) = delete;Resource(Resource&& other) noexcept : id_(other.id_) { other.id_ = kErrorId;}Resource& operator=(Resource&& other) noexcept { id_ = other.id_; other.id_ = kErrorId; return *this;}private:int id_;};constexpr int kPoolSize = 3;ResourcePool<Resource> pool(kPoolSize); // Resource pool with 3 resources in global scope.void RequestRourceTest() {constexpr int kResourcesNum = 3;for (int i = 0; i < kResourcesNum; ++i) {ResourceWrapper<Resource> resource_wrapper(pool);resource_wrapper.GetRawResource();}}int main() {RequestRourceTest();return 0;}

運(yùn)行輸出結(jié)果如下:

代碼語言:javascript
復(fù)制
Resource 0 is created.Resource 1 is created.Resource 2 is created.Resource 0 is acquired.Resource 0 is released.Resource 1 is acquired.Resource 1 is released.Resource 2 is acquired.Resource 2 is released.

5. 總結(jié)

在本文中,我們介紹了C++中的RAII技術(shù),,它是一種管理資源的方法,,可以幫助我們避免內(nèi)存泄漏和資源泄漏等問題。RAII技術(shù)的核心思想是將資源的獲取和釋放綁定在對象的生命周期中,,這樣可以確保資源在不再需要時(shí)被正確釋放。我們還介紹了如何使用RAII技術(shù)來管理動(dòng)態(tài)內(nèi)存,、文件句柄和互斥鎖等資源,,并提供了一些示例代碼來說明如何實(shí)現(xiàn)RAII類。最后,我們還討論了RAII技術(shù)的一些注意事項(xiàng)和最佳實(shí)踐,,以幫助開發(fā)人員編寫更安全,、更可靠的代碼。希望本文能夠幫助您更好地理解和應(yīng)用RAII技術(shù),。

在本文的編程實(shí)踐中,,還使用了std::move()、std::forward()等諸多現(xiàn)代C++技術(shù),,更多細(xì)節(jié)和不足之處將在之后的文章中進(jìn)行進(jìn)一步探討,。

參考:

  1. Effective C++, Item 13: Use objects to manage resources. Scott Meyers.

  2. https://en./w/cpp/language/raii

你好,我是七昂,,計(jì)算機(jī)科學(xué)愛好者,,致力于分享C/C++、操作系統(tǒng)等計(jì)算機(jī)基礎(chǔ)知識,。希望我們能一起在計(jì)算機(jī)科學(xué)的世界里探索和成長,,最終能站得更高,走得更遠(yuǎn),。 

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多