2010-02-23 38 views
156

什麼意思是資源獲取是初始化(RAII)?什麼意思是資源獲取是初始化(RAII)?

+11

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization – 2010-02-23 20:39:53

+8

這是驅動它回家的東西。 http://www.stroustrup.com/bs_faq2.html#finally – 2013-05-21 22:45:41

+1

微軟提供3個句子和2個例子,但非常清楚! https://msdn.microsoft.com/en-us/library/hh438480.aspx – 2016-11-17 21:58:06

回答

85

這是一個編程成語,簡單地意味着你

  • 封裝的資源放到一個類(其構造通常 - 但不一定** - 獲取資源,析構函數總是釋放它)
  • 通過類的本地實例使用資源*
  • 當對象獲得超出範圍的資源會自動釋放

這保證日無論在資源使用時發生什麼,它最終都會被釋放(無論是由於正常返回,包含對象的銷燬還是拋出的異常)。

這是一個在C++中廣泛使用的良好實踐,因爲除了處理資源的安全方式之外,它還使代碼更加清潔,因爲您不需要將錯誤處理代碼與主要功能混合在一起。

*更新:「本地」可能表示局部變量或類的非靜態成員變量。在後一種情況下,成員變量被其所有者對象初始化並銷燬。

**Update2:正如@sbi指出的那樣,資源 - 儘管經常在構造函數中分配 - 也可以在外部分配並作爲參數傳入。

+0

AFAIK,首字母縮寫詞並不意味着對象必須位於本地(堆棧)變量上。它可能是另一個對象的成員變量,所以當'hold'對象被銷燬時,成員對象也被銷燬,資源被釋放。事實上,我認爲這個首字母縮寫詞的具體含義是,沒有'open()'''close()'方法來初始化和釋放資源,只是構造函數和析構函數,所以資源的「持有」只是生命週期無論該生命週期是由上下文(棧)還是顯式地(動態分配)來處理 – Javier 2010-02-23 20:51:32

+1

實際上沒有說資源必須在構造函數中獲取。文件流,字符串和其他容器都是這樣做的,但是資源也可能被傳遞給構造函數,就像智能​​指針通常一樣。既然你是最有回報的答案,你可能想解決這個問題。 – sbi 2012-10-08 20:59:19

+0

它不是一個縮寫,它是一個縮寫。 IIRC大多數人發音爲「ar ey ay ay」,所以它不符合DARPA這樣的縮寫詞,DARPA是發音爲DARPA而不是拼寫。另外,我會說RAII是一個範例,而不僅僅是一個成語。 – dtech 2013-08-05 10:24:00

37

「RAII」代表「資源採集是初始化」,實際上是一個相當用詞不當,因爲它不是資源收購(和對象的初始化),它關注的是,但釋放資源(通過對象的破壞)。
但RAII是我們得到的名字,它堅持。

在它的心臟地帶,成語功能封裝資源(內存塊,打開文件,鎖定互斥,你-名稱吧)在地方,自動對象,以及具有物體釋放的析構函數資源對象時在範圍的端部被破壞它屬於:

{ 
    raii obj(acquire_resource()); 
    // ... 
} // obj's dtor will call release_resource() 

當然,對象不是總是本地的,自動的對象。他們也可能是一類的成員:

class something { 
private: 
    raii obj_; // will live and die with instances of the class 
    // ... 
}; 

如果這些對象管理內存,它們通常被稱爲「智能指針」。

這有很多變化。例如,在第一個代碼片段中,如果有人想複製obj,會出現什麼情況。最簡單的方法是簡單地禁止複製。 std::unique_ptr<>,這是一個智能指針,作爲下一個C++標準所特有的標準庫的一部分。
另一個這樣的智能指針std::shared_ptr具有它擁有的資源(一個動態分配的對象)的「共享所有權」。也就是說,它可以自由複製,並且所有副本都指向同一個對象。智能指針會記錄有多少副本引用同一個對象,並在最後一個被銷燬時將其刪除。
第三個變體的特徵是std::auto_ptr,它實現了一種移動語義:一個對象只有一個指針,並且嘗試複製一個對象將導致(通過語法hackery)將對象的所有權轉移到複製操作。

+3

'std :: auto_ptr'是'std :: unique_ptr'的過時版本。 'std :: auto_ptr'類似於C++ 98中可能的模擬移動語義,'std :: unique_ptr'使用C++ 11的新移動語義。創建新類是因爲C++ 11的移動語義更加明確(除了臨時,需要'std :: move'),而在默認情況下,它是爲'std :: auto_ptr'中非const的任何副本創建的。 – 2013-08-05 09:40:26

8

本書C++ Programming with Design Patterns Revealed介紹RAII爲:

  1. 獲取所有資源
  2. 利用資源
  3. 釋放資源

  • 資源是impleme nted爲類,所有指針都有類包裝(使它們成爲智能指針)。

  • 通過調用其構造函數獲取資源,並通過調用它們的析構函數隱式釋放(以獲取的相反順序)。

+1

@Brandin我編輯了我的文章,以便讀者將重點放在重要的內容上,而不是辯論關於合理使用版權法的灰色地帶。 – Dennis 2016-05-29 13:33:40

237

這是一個令人難以置信的強大的概念,一個真正可怕的名字,也許數量1東西,C++開發人員時,他們切換到其他語言錯過一個。雖然它似乎還沒有被發現,但試圖將這個概念重新命名爲作用域綁定資源管理有一些動作。

當我們說'資源'時,我們不只是指記憶 - 它可能是文件句柄,網絡套接字,數據庫句柄,GDI對象......總之,我們有限的資源,所以我們需要的東西能夠控制他們的使用。 '範圍綁定'方面意味着對象的生命週期被綁定到變量的範圍,所以當變量超出範圍時,析構函數將釋放資源。這是一個非常有用的特性,它提高了異常安全性。例如,比較這:

RawResourceHandle* handle=createNewResource(); 
handle->performInvalidOperation(); // Oops, throws exception 
... 
deleteResource(handle); // oh dear, never gets called so the resource leaks 

隨着RAII一個

class ManagedResourceHandle { 
public: 
    ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {}; 
    ~ManagedResourceHandle() {delete rawHandle; } 
    ... // omitted operator*, etc 
private: 
    RawResourceHandle* rawHandle; 
}; 

ManagedResourceHandle handle(createNewResource()); 
handle->performInvalidOperation(); 

在後一種情況下,當發生異常時,堆棧退繞時,局部變量被銷燬,確保我們的資源被清理並且不泄漏。

+10

非常清晰,而不僅僅是概念。謝謝。 – 2014-03-03 04:46:01

+59

+1「這是一個非常可怕的名字」 – deinocheirus 2014-04-03 14:46:27

+0

@the_mandrill:我試過ideone.com/1Jjzuc這個程序。但是沒有析構函數調用。 tomdalling.com/blog/software-design/...表示,C++保證即使拋出異常也會調用堆棧中對象的析構函數。那麼,爲什麼析構函數沒有在這裏執行?我的資源是否泄露或者永遠不會被釋放或釋放? – Destructor 2015-08-20 16:40:07

1

手動內存管理是程序員自發明編譯器以來一直在發明的避免方法的噩夢。使用垃圾收集器的編程語言讓生活變得更容易,但代價是性能。在本文 - Eliminating the Garbage Collector: The RAII Way中,Toptal工程師Peter Goodspeed-Niklaus向我們介紹了垃圾收集器的歷史,並解釋了所有權和借款的概念如何幫助消除垃圾收集器而不損害其安全保證。