1. 程式人生 > >Boost學習系列2-智慧指標(上)

Boost學習系列2-智慧指標(上)

 一、概述

    最先講的就是指標,這是C語言中,不少程式設計師害怕的東西,害怕的原因大多是因為不瞭解其初始化、呼叫、賦值和清除的方式,而智慧指標則可以去除這個顧慮,在初始化時就已經預定了刪除,排解了後顧之憂。1998年修訂的第一版C++標準只提供了一種智慧指標:std::auto_ptr,它基本上就像是個普通的指標:通過地址來訪問一個動態分配的物件。std::auto_ptr之所以被看作是智慧指標,是因為它會在析構的時候呼叫delete操作符來自動釋放所包含的物件。當然這要求在初始化的時候,傳給它一個由new操作符返回的物件的地址。既然std::auto_ptr的解構函式會呼叫delete操作符,它所包含的物件的記憶體會確保釋放掉。這是智慧指標的一個優點。

當嘗試和異常聯絡起來時這就更加重要了:沒有std::auto_ptr這樣的智慧指標,每一個動態分配記憶體的函式都需要捕捉所有可能的異常,以確保在異常傳遞給函式的呼叫者之前將記憶體釋放掉。Boost C++ 庫的智慧指標系列提供了許多可以用在各種場合的智慧指標。

二、RAII

    先介紹下一個專業詞彙:RAII(Resource Application Immediately Initialize)資源申請即初始化。這也是智慧指標的基本原理,智慧指標只是這個習語的其中一例。智慧指標確保在任何情況下,動態分配的記憶體都能得到正確釋放,從而將開發人員從這項任務中解放了出來。 這包括程式因為異常而中斷,原本用於釋放記憶體的程式碼被跳過的場景。用一個動態分配的物件的地址來初始化智慧指標,在析構的時候釋放記憶體,就確保了這一點。因為解構函式總是會被執行的,這樣所包含的記憶體也將總是會被釋放。

無論何時,一定得有第二條指令來釋放之前另一條指令所分配的資源時,RAII 都是適用的。許多的 C++ 應用程式都需要動態管理記憶體,因而智慧指標是一種很重要的 RAII 型別。不過 RAII 本身是適用於許多其它場景的。

下面例子中的這個類就利用了這樣的機制:

#include <windows.h> 

class windows_handle 
{ 
  public: 
    windows_handle(HANDLE h) 
      : handle_(h) 
    { 
    } 

    ~windows_handle() 
    { 
      CloseHandle(handle_); 
    } 

    HANDLE handle() const 
    { 
      return handle_; 
    } 

  private: 
    HANDLE handle_; 
}; 

int main() 
{ 
  windows_handle h(OpenProcess(PROCESS_SET_INFORMATION, FALSE, GetCurrentProcessId())); 
  SetPriorityClass(h.handle(), HIGH_PRIORITY_CLASS); 
} 

它定義了一個名為windows_handle的類,其解構函式呼叫了CloseHandle()函式。這是一個Windows API函式,因而這個程式只能在Windows上執行。在Windows上,許多資源在使用之前都要求開啟,這意味著資源不再使用之後就應該關閉。windows_handle 類的機制能確保這一點。

    它的例項h是以一個控制代碼來初始化。例子中的OpenProcess函式沒有什實際意義,就是幫助給類的例項賦值並使用的。重點是通過OpenProcess開啟的資源不需要顯示的呼叫 CloseHandle來關閉。當然,應用程式終止時資源也會隨之關閉。然而,在更加複雜的應用程式裡,windows_handle類確保當一個資源不再使用時就能正確的關閉。某個資源一旦離開了它的作用域(上例中 h 的作用域在main函式的末尾)它的解構函式會被自動的呼叫,相應的資源也就釋放掉了。這也就是RAII的精髓所在。

三、Boost的智慧指標

3.1作用域指標

     一個作用域指標獨佔一個動態分配的物件。對應的類名為boost::scoped_ptr,它的定義在boost/scoped_ptr.hpp中。不像std::auto_ptr,一個作用域指標不能傳遞它所包含的物件的所有權到另一個作用域指標。一旦用一個地址來初始化,這個動態分配的物件將在析構階段釋放。因為一個作用域指標只是簡單儲存和獨佔一個記憶體地址,所以boost::scoped_ptr的實現就要比std::auto_ptr簡單。在不需要所有權傳遞的時候應該優先使用boost::scoped_ptr。在這些情況下,比起std::auto_ptr它是一個更好的選擇,因為可以避免不經意間的所有權傳遞。下面這個例子簡單描述了scope_ptr的使用方法:

#include <boost/scoped_ptr.hpp> 

int main() 
{ 
  boost::scoped_ptr<int> i(new int); 
  *i = 1; 
  *i.get() = 2; 
  i.reset(new int); 
} 

一經初始化,智慧指標boost::scoped_ptr所包含的物件可以通過類似於普通指標的介面來訪問。這是因為過載了相關的操作符operator*(),operator->() 和 operator bool()。此外,還有get和reset方法。前者返回所含物件的地址,後者用一個新的物件來重新初始化智慧指標。在這種情況下,新建立的物件賦值之前會先自動釋放所包含的物件。

     boost::scoped_ptr的解構函式中使用delete操作符來釋放所包含的物件。這對 boost::scoped_ptr所包含的型別加上了一條重要的限制。它不能用動態分配的陣列來做初始化,因為這需要呼叫delete[]來釋放。在這種情況下,可以使用下面將要介紹的 boost:scoped_array類。

3.2、作用域陣列

    作用域陣列的使用方式與作用域指標相似。關鍵不同在於,作用域陣列的解構函式使用 delete[]操作符來釋放所包含的物件。因為該操作符只能用於陣列物件,所以作用域陣列必須通過動態分配的陣列來初始化。對應的作用域陣列類名為boost::scoped_array,它的定義在boost/scoped_array.hpp 裡。例子和上面類似,只是操作物件成了陣列:

#include <boost/scoped_array.hpp> 

int main() 
{ 
  boost::scoped_array<int> i(new int[2]); 
  *i.get() = 1; 
  i[1] = 2; 
  i.reset(new int[3]); 
}

boost:scoped_array類過載了操作符operator[]和operator bool。可以通過operator[]操作符訪問陣列中特定的元素,於是boost::scoped_array型別物件的行為就酷似它所含的陣列。正如boost::scoped_ptr那樣,boost:scoped_array也提供了get和reset方法,用來返回和重新初始化所含物件的地址。

3.3、共享指標

    這是使用率最高的智慧指標,但是 C++ 標準的第一版中缺少這種指標。它已經作為技術報告1(TR 1)的一部分被新增到標準裡了。如果開發環境支援的話,可以使用 memory 中定義的 std::shared_ptr。在Boost C++庫裡,這個智慧指標命名為boost::shared_ptr,定義在boost/shared_ptr.hpp裡。

    智慧指標boost::shared_ptr基本上類似於boost::scoped_ptr。關鍵不同之處在於 boost::shared_ptr 不一定要獨佔一個物件。它可以和其他boost::shared_ptr型別的智慧指標共享所有權。 在這種情況下,當引用物件的最後一個智慧指標銷燬後,物件才會被釋放。因為所有權可以在boost::shared_ptr之間共享,任何一個共享指標都可以被複制,這跟 boost::scoped_ptr是不同的。這樣就可以在標準容器裡儲存智慧指標了——你不能在標準容器中儲存std::auto_ptr,因為它們在拷貝的時候傳遞了所有權。

    因為 boost::shared_ptr 能夠共享它所含物件的所有權,所以儲存在容器中的拷貝(包括容器在需要時額外建立的拷貝)都是和原件相同的。如前所述,std::auto_ptr做不到這一點,所以絕對不應該在容器中儲存它們。正是有了它,我們才可以在標準容器中安全的使用動態分配的物件:

#include <boost/shared_ptr.hpp> 
#include <vector> 

int main() 
{ 
  std::vector<boost::shared_ptr<int> > v; 
  v.push_back(boost::shared_ptr<int>(new int(1))); 
  v.push_back(boost::shared_ptr<int>(new int(2))); 

  boost::shared_ptr<int> i1(new int(1)); 
  boost::shared_ptr<int> i2(i1); 
  i1.reset(new int(2));
}

類似於boost::scoped_ptr、boost::shared_ptr類過載了以下這些操作符:operator*、operator-> 和operator bool,另外還有get和reset函式來獲取和重新初始化所包含的物件的地址。上例中定義的2個共享指標i1i2都引用到同一個int型別的物件。i1通過new操作符返回的地址顯示的初始化,i2通過i1拷貝構造而來。i1接著呼叫reset,它所包含的整數的地址被重新初始化,不過它之前所包含的物件並沒有被釋放,因為i2仍然引用著它。智慧指標 boost::shared_ptr記錄了有多少個共享指標在引用同一個物件,只有在最後一個共享指標銷燬時才會釋放這個物件。

    預設情況下,boost::shared_ptr 使用delete操作符來銷燬所含的物件。然而,具體通過什麼方法來銷燬是可以指定的,就像下面的例子:

#include <boost/shared_ptr.hpp> 
#include <windows.h> 

int main() 
{ 
  boost::shared_ptr<void> h(OpenProcess(PROCESS_SET_INFORMATION, FALSE, GetCurrentProcessId()), CloseHandle); 
  SetPriorityClass(h.get(), HIGH_PRIORITY_CLASS); 
}

共享指標的建構函式第二個引數是一個函式物件CloseHandle,當h超出其作用域時,將不再呼叫delete了。

3.4、共享陣列

   共享陣列的行為類似於共享指標,方式則類似scoped_array,在此不多贅述。



相關推薦

Boost學習系列2-智慧指標()

 一、概述    最先講的就是指標,這是C語言中,不少程式設計師害怕的東西,害怕的原因大多是因為不瞭解其初始化、呼叫、賦值和清除的方式,而智慧指標則可以去除這個顧慮,在初始化時就已經預定了刪除,排解了後顧之憂。1998年修訂的第一版C++標準只提供了一種智慧指標:std::a

Boost學習系列2-智慧指標(下)

 3.5、弱指標    前面的幾種智慧指標在不同場合可以獨立使用,然而,弱指標只有在配合共享指標使用時才會有意義,見下面例子:#include <windows.h> #include <boost/shared_ptr.hpp> #include

Hadoop學習系列(2.Hadoop框架介紹與搜索技術體系介紹)

消息 監控系統 mapreduce spa 文件系統 sql 平時 偽分布式 自己 第一天2.Hadoop框架介紹與搜索技術體系介紹1.大數據典型特性與分布式開發難點2.Hadoop框架介紹與搜索技術體系介紹3.Hadoop版本與特性介紹4.Hadoop核心模塊之HDFS分

Git學習系列2 初配置及結構

一 初配置 安裝完成之後,在開始選單裡面找到 "Git --> Git Bash",如下 需要配置使用者名稱和郵箱,如果不清楚是否已配置,可用 git config user.name 和git config user.email進行檢視。 如果沒有進行配置,用gi

C++學習筆記5_智慧指標

1. 一般的指標int main(void){ int *p=new int; *p=20; delete p; return 0;}智慧指標能自動回收#include<memory> 記得要引用標頭檔案int main(void){ //auto_ptr<int>模板寫法 auto_

Spring Boot 2+gRPC 學習系列2:搭建Spring Cloud +gRPC叢集專案

本專案基於Spring Boot 2.0.5+yidongnan/grpc-spring-boot-starter 2.0.1.RELEASE+SpringCloud Finchley.SR1,通過2個grpc-eureka-server模擬Eureka叢集,

SonarQube學習系列2:Maven+SonarQube 最佳實踐

本文記錄了Maven工程使用SonarQube完成程式碼評估,並對其中指定模組進行排除和專案許可權管理等 有些程式碼是使用相關外掛或工具生成的,這些程式碼通常存在高冗餘或書寫不規範現象,不符合程式碼質量要求,但不影響使用,應排除在程式碼質量評估之外 本文基於上一

[GAN學習系列2] GAN的起源

本文大約 5000 字,閱讀大約需要 10 分鐘 這是 GAN 學習系列的第二篇文章,這篇文章將開始介紹 GAN 的起源之作,鼻祖,也就是 Ian Goodfellow 在 2014 年發表在 ICLR 的論文–Generative Adversarial

建議慎用boost::weak_ptr來避免智慧指標迴圈引用

  為了降低智慧指標迴圈引用的可能性,boost智慧指標引入了weak_ptr(各版本的智慧指標實現基本上都有這個概念)。weak_ptr在構造/析構的時候不會增加/減少引用計數,由於不會增加引用計數,所以與普通智慧指標(有些實現成為strong智慧指標呵呵)相比,它就沒法保證hold住物件,所以當要使用we

C++“準”標準庫Boost學習指南(2):Boost.Conversion

原文地址 Conversion庫包含有一些函式,它們是現有的強制型別轉換操作符(static_cast, const_cast, 和  dynamic_cast)的增強。Conversion為安全的多型轉換增加了 polymorphic_cast 和 polymorphic

深度學習系列文章之二:win7+Ubantu雙系統裝機步驟(硬碟安裝)

一次上傳總是傳不上去,所以將安裝步驟分為上中下三篇上傳。 64位Win7系統下安裝ubantu14.04雙系統 一.安裝所需軟體 1、分割槽助手專業版(必需):用來對硬碟分割槽,將磁碟的一部分格式化成Linux可以識別的ext3格式。 2、Ext2Fsd(硬碟安裝必需,光

[學習操練]C++智慧指標類的簡單實現(類模板實現)

問題的提出 在使用指標的過程中,我們有時需要動態的分配指標,在c++中,我們推薦使用new分配記憶體,delete釋放記憶體。在編寫大型程式的時候,有時我們忘記delete分配的記憶體,或者多次釋放分配的記憶體,這樣會造成記憶體洩露和程式的down機。 解決方案 在程式開發中

GANs學習系列(2):cGANs:Conditional Generative Adversarial Networks

結論by ym: 轉完這篇,理解一下表達一下自己的一點感想。 其實cgan潛在的道理很簡單,但是這個簡單直接的改進被證明非常有效。 簡單來說就是因為: 在生成模型中,先驗輸入噪聲p(z)和條件資訊y聯合組成了聯合隱層表徵。對抗訓練框架在隱層表徵的組成方式方面相當地靈活。 即cnn一直以來的

學習筆記之智慧指標和執行緒安全內容筆記

使用shared_ptr控制物件的生命週期,常用來進行物件的建立,屬於強引用,只要被shared_ptr引用該物件就不會被析構 weak_ptr是一種弱引用,常常用來偵查物件是否存在,不控制物件的生命期,也不會增加物件的引用計數如果物件還存在沒被析構那麼可以通過成員函式進行

點雲深度學習系列2——PointNet/PointCNN程式碼比較(變換矩陣部分)

PointNet與PointCNN從文章到程式碼都有很多相似之處,兩者對比看待,或許更有助於我們理解。 眾所周知,PointNet中使用了maxpooling和T-net,作者文章中起到關鍵作用的是maxpooling,而T-net對效能的提升作用也還是有的(兩個T-net

C++深度探索系列智慧指標(Smart Pointer) [二]

                                           深度探索智慧指標(Smart Pointer) 主題索引: 一、剖析C++標準庫智慧指標(std::auto_ptr)        1.Do you Smart Pointer?    2

C++Boost學習智慧指標 shared_ptr

目錄 1.共享指標shared_ptr ^   使用例子 ^ #include<boost/shared_ptr.hpp> using namespace boost; using std::cout; using std::endl; str

boost學習智慧指標

scoped_ptr  也叫作用域指標,一個指標獨佔一個動態分配的物件。對應的類名為 boost::scoped_ptr,它的定義在 boost/scoped_ptr.hpp 中。 與 std::auto_ptr 區別:不能將所指物件的所

Boost學習筆記(二)- 智慧指標

概述 1.智慧指標的原理基於一個常見的習語叫做 RAII :資源申請即初始化。 2.智慧指標確保在任何情況下,動態分配的記憶體都能得到正確釋放,從而將開發人員從這項任務中解放了出來。 這包括程式因為異常而中斷,原本用於釋放記憶體的程式碼被跳過的場景。 3.一個作用域指標不能傳遞它所包含的

小和尚上山學習智慧指標(六)--boost::weak_ptr

boost::weak_ptr 屬於 boost 庫,定義在 namespace boost 中,包含標頭檔案 #include<boost/smart_ptr.hpp> 便可以使用。在講 boost::weak_ptr 之前,讓我們先回顧一下前面講解的內容。似乎