讀書筆記 effective c++ Item 14 對資源管理類的拷貝行為要謹慎

来源:http://www.cnblogs.com/harlanc/archive/2017/02/19/6414789.html
-Advertisement-
Play Games

1. 自己實現一個資源管理類 Item 13中介紹了 “資源獲取之時也是初始化之時(RAII)”的概念,這個概念被當作資源管理類的“脊柱“,也描述了auto_ptr和tr1::shared_ptr是如何用堆資源來表現這個概念的。然而並不是所有資源都是在堆上創建的,對於這種資源,像auto_ptr和t ...


1. 自己實現一個資源管理類 

Item 13中介紹了 “資源獲取之時也是初始化之時(RAII)”的概念,這個概念被當作資源管理類的“脊柱“,也描述了auto_ptr和tr1::shared_ptr是如何用堆資源來表現這個概念的。然而並不是所有資源都是在堆上創建的,對於這種資源,像auto_ptr和tr1::shared_ptr這樣的智能指針就不適合當作資源句柄(handle)來使用了。你會發現你時不時的就會需要創建自己的資源管理類。

舉個例子,假設你正在使用C API來操縱Mutex類型的互斥信號量對象,來為函數提供lock和unlock:

1 void lock(Mutex *pm); // lock mutex pointed to by pm
2 
3 void unlock(Mutex *pm); // unlock the mutex

為了確保你不會忘記unlock一個已經加過鎖的Mutex,你需要創建一個類來管理鎖。這樣一個類的基本結構已經由RAII準則表述過了,也就是資源會在執行構造的時候獲取到,在執行析構的時候釋放掉

 1 class Lock {
 2 
 3 public:
 4 
 5 explicit Lock(Mutex *pm)
 6 
 7 : mutexPtr(pm)
 8 
 9 { lock(mutexPtr); } // acquire resource
10 
11 ~Lock() { unlock(mutexPtr); } // release resource
12 
13 private:
14 
15 Mutex *mutexPtr;
16 
17 };

 

客戶端以傳統的RAII方式來使用鎖:

 1 Mutex m; // define the mutex you need to use
 2 
 3 ...
 4 
 5 { // create block to define critical section
 6 
 7 Lock ml(&m); // lock the mutex
 8 
 9 ... // perform critical section operations
10 
11 } // automatically unlock mutex at end
12 
13 // of block

 

2. 對資源管理類進行拷貝會發生什麼?

這很好,但如果一個鎖對象被拷貝會發生什麼呢?

1 Lock ml1(&m); // lock m
2 
3 Lock ml2(ml1); // copy ml1 to ml2 — what should
4 
5 // happen here?

 

上面是一個更加普通的問題,也是每個RAII類的作者必須面對的:當一個RAII對象被拷貝的時候應該發生什麼呢?大多數情況下,你將會從下麵的4種可能中選擇一個:

2.1 禁止拷貝

  • 禁止拷貝。在許多情況下,允許RAII對象被拷貝是沒有意義的。對於一個像Lock的類來說這可能是真的,因為一份同步原語(synchronization primitives)的拷貝很少情況下是有意義的。當一個RAII類的拷貝沒有意義時,你應該禁止它。Item 6解釋瞭如何可以做到:將拷貝操作聲明稱private。對於Lock來說,可以是下麵這個樣子:
1 class Lock: private Uncopyable { // prohibit copying — see
2 
3 public: // Item 6
4 
5 ... // as before
6 
7 };

 

2.2 一份資源,多次引用——使用tr1::shared_ptr

  • 對底層資源進行引用計數。有時候需要保留一個資源直到引用這個資源的最後一個對象被銷毀。在這種情況下,拷貝一個RAII對象應該增加對象引用資源的引用計數。這就是用tr1::shared_ptr進行“拷貝”的含義。

 

     通常情況下,RAII類可以通過包含一個tr1::shared_ptr數據成員來實現引用計數的拷貝行為。舉個例子,如果Lock想使用引用計數,它可以將mutexPtr的類型從Mutex*改為tr1::shared_ptr<Mutex>。不幸的是,tr1::shared_ptr的預設行為是當引用技術為0的時候會刪除它所指向的資源,這不是我們想要的。當我們實現一個Mutex類時,我們只是想unlock,並不想刪除它們。幸運的是,tr1::shared_ptr允許指定自己的刪除器(”deleter”)---一個函數或者函數對象,引用計數為0的時候會自動調用這個對像。(auto_ptr中不存在這個功能,它總是會刪除指針。)這個刪除器是tr1::shared_ptr構造函數的第二個可選參數,所以代碼會是下麵這個樣子:

 1 class Lock {
 2 
 3 public:
 4 
 5 explicit Lock(Mutex *pm) // init shared_ptr with the Mutex
 6 
 7 : mutexPtr(pm, unlock) // to point to and the unlock func
 8 
 9 { // as the deleter†
10 
11 lock(mutexPtr.get()); // see Item 15 for info on “get”
12 
13 }
14 
15 private:
16 
17 std::tr1::shared_ptr<Mutex> mutexPtr; // use shared_ptr
18 
19 }; // instead of raw pointer

 

註意在這個例子中,Lock類不再聲明析構函數。因為沒有必要了。Item 5 解釋到一個類的析構函數(無論是編譯器生成的還是用戶定義的)會自動調用類中的非靜態數據成員的析構函數。在這個例子中,非靜態數據成員為mutexPtr。但是在mutex的引用計數為0的時候其的析構函數會自動調用tr1::shared_ptr的刪除器—也即是unlock。(人們在看到類的源碼的時候如果有一行註釋來說明你沒有忘記析構,你只是使用了編譯器預設生成的析構函數,他們會很感激的。)

2.3 一份資源,多次拷貝——深拷貝

  • 拷貝底層的資源。有時你可以擁有一個資源儘可能多的拷貝,你需要一個資源管理類的唯一原因是能夠確保資源被使用完畢後能夠被釋放掉。這種情況下,拷貝一個資源管理對象應該同時拷貝他所包裹(wraps)的資源。也就是拷貝一個資源管理類對象需要執行“深拷貝”。

有一些標準string類型的實現中包含了指向堆記憶體的指針,組成string的字元會保存在這塊記憶體中。當一個string對象被拷貝的時候,會同時拷貝指針和指針指向的記憶體。這樣的string展示出來的是深拷貝。

2.4 一份資源,一次引用,轉移所有權——使用auto_ptr

  • 轉移底層資源的所有權。在很少的場合,你可能需要確保只有一個RAII對象指向一個原生(raw)資源,所以當RAII對象被拷貝的時候,資源的擁有權從被拷貝對象轉移到了拷貝到的對象。正如Item 13所解釋的,這是使用auto_ptr進行拷貝的含義。 

拷貝函數可能由編譯器生成,所以除非編譯器生成版本能夠做到你想要的(Item 5解釋了預設版本的行為),否則你需要自己實現它們。一些情況下你可能想支持這些函數的一般版本。這些版本在Item 45進行描述。

3. 總結

  • 拷貝一個RAII對象需要拷貝他所管理的資源,因此資源的拷貝行為決定了RAII對象的拷貝行為。
  • 普通RAII類的拷貝行為是禁止拷貝,執行引用計數,但其他拷貝行為也是可以實現的。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 測試: vim /etc/hosts ##ip地址轉換 修改windows 的配置文件,寫字板打開 vim /usr/local/apache/conf/httpd.conf vim /usr/local/apache/conf/extra/httpd-vhosts.conf mkdir /data ...
  • tags: MacPorts 錯誤故障 _環境: macOS 10.x,Terminal_ 問題表現 在使用 port 命令時,出現這樣的錯誤提示: 解決辦法 此問題是“port”命令(即 MacPorts 工具)無法工作。可能是因為 macOS 系統升級,Macports 沒有升級造成。 __手動 ...
  • vim /usr/local/apache/conf/httpd.conf ##修改apache的網頁配置文件 → 解析php文件 /usr/local/apache/bin/apachectl graceful ## 重新載入 vim 2.php vim info.php cp /usr/loca ...
  • 1. 安裝 Docker Engine 2. 添加用戶到docker group 預設安裝完 Docker 後,每次執行 docker 都需要運行 sudo 命令,非常浪費時間影響效率。如果不跟 sudo,直接執行 docker images 命令會報沒有許可權的問題 如果想免 sudo 使用 doc ...
  • 分享一篇關於linux文件系統inode講解得比較清晰的文章~ inode是一個重要概念,是理解Unix/Linux文件系統和硬碟儲存的基礎。 我覺得,理解inode,不僅有助於提高系統操作水平,還有助於體會Unix設計哲學,即如何把底層的複雜性抽象成一個簡單概念,從而大大簡化用戶介面。 下麵就是我 ...
  • 獲取資源: cd /usr/local/src/ wget http://cn2.php.net/distributions/php-5.4.45.tar.bz2 tar jxvf php-5.4.45.tar.bz2 環境配置: rpm -ivh 'http://www.lishiming.net ...
  • 本文為搭建過程中隨筆隨記,僅供參考。 本人也是初識linux系統的新手,很多搭建過程中,也是學習的過程,邊搭建邊搜索相關的資料。 部署Seafile伺服器(使用MySQL) 安裝配置MySQL,具體過程不再描述。 研究Seafile伺服器手冊,地址: 閱讀研究相應的文檔,依據步驟一步步搭建。 前幾步 ...
  • 1.獲取源碼包 cd /usr/local/src/ ls wget http://mirrors.cnnic.cn/apache/httpd/httpd-2.4.25.tar.gz 2.解壓、編譯、安裝 tar zxvf httpd-2.4.25.tar.gz cd httpd-2.4.25 ca ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...