為什麼C++中的RAII類一般都是不可複製的?
其實不是 RAII 類一般不可複製,而是大多數類一般都不可複製。鑒於 C++ 各種隱式的拷貝、構造過程容易產生混淆,常見的簡單可靠的做法是盡量禁用隱式的複製行為,而讓有複製需求的類實現 clone() 方法。
這樣可以用最簡單幹凈的語義(資源有效期=對象生命周期)覆蓋大部分使用場景。剩下的場景可以用別的方法解決。正所謂殺雞焉用牛刀,雖然兩把刀看起來比一把刀複雜,但程序行為更容易把握。
真要複製當然有辦法:
1. shared_ptr。基於上述理由,shared_ptr&那句話怎麼說的來著?所有跳過「是不是」直接問「為什麼」的都是耍流氓。一個類有沒有用 RAII 和它可不可複製並沒有直接關係。
不信,你看標準庫里常用的 std::vector、std::map、std::string、std::shared_ptr 等等不都是用了 RAII 且可以複製的嗎?
RAII 只是一種資源管理的技巧而已,為的是把資源的生命周期和變數的生命期綁定起來,並沒有是否可複製的約束:- 實現時,把資源封裝進類:
- 構造函數:獲取資源,並完成其它初始化。無法完成時拋出異常。
- 析構函數:釋放資源。
- 使用時,變數應該具有自動存儲期。
- 值語義:關心這個對象所表示的值。在複製時,類似於使用普通的 int,使用的是其值,複製前後的對象沒有關係。例如 std::string 對象就是值語義的,雖然使用了 RAII,但是允許複製。
- 引用語義:關心對象本身。複製一個具有引用語義的對象通常是沒有意義的。例如 std::thread 對象,代表一個線程,複製這個對象的話,理論上需要創建一個完全相同的線程才行。所以 std::thread 雖然使用了 RAII,但是不允許複製。
當然,上面說的其實都是「理論上該不該可複製」。實際實現時,決定某個類是否可複製的,除了以上這些理論上的東西,還會考慮實現起來必不必要、麻煩不麻煩、中午吃得飽不飽、目前心情好不好、晚上是滿月還是弦月等等因素……
最後,如果不清楚某個類是否應該可複製,默認把它寫成不可複製,必要時才讓它可複製,通常是一個不錯的做法。RAII類通常是用來管理資源的,而一般來說,資源管理從概念上講是獨佔性的。
這是由於資源本身的特殊性造成的。資源一般來說是一個句柄(或者說指針),它的生存周期是由它的持有者(管理者)所決定的。那麼當存在多個管理者管理同一個資源時,相互之間就必須要進行一些協調工作。
RAII類屬於簡單的資源管理者,類和類之間一般來說是不會有複雜的協調工作的。因此當一個RAII類獲得了一個資源之後,實際上獨佔了該資源的管理權(所有權)。這樣在自身析構的時候,只需要進行簡單的釋放工作。如果RAII類可以複製,那麼我們就必須要求它具有共享的資源管理能力 —— 例如std::shared_ptr,使用引用計數作為資源管理者之間的協調機制。否則在RAII類想釋放它管理的資源時,如何保證沒有其它類持有這個資源呢?
因此,對於非共享的RAII類,即構造時獲得資源,析構時簡單釋放的類,通常只應該具有移動語義,而不應該具備拷貝語義。以內存為例,RAII類會在構造函數中申請內存,在析構時自動釋放,如果可以複製就會釋放兩次
推薦閱讀:
※如何評價博客園上的博文《 開發人員要亡新浪微博,你攔都攔不住!》?
※C/C++ 中 0 與 NULL 區別是什麼?用 delete 時,用 p=0,還是用 p=NULL 好?為什麼?
※考慮R值引用,c++下多字元串連接,如何寫更高效?
※C#轉C++開發,該歷經怎樣的學習路線?
※如何評價Qt Lite Project?
TAG:C |