Memory_(C++标准库)

**memory** 是 C++ 標準程式庫中的一個標頭檔，定義了 C++ 標準中的智慧型指標、記憶體分配器（allocator）、與垃圾回收有關的函式、臨時性的申請與釋放動態記憶體的函式、在記憶體上建立（構造）物件的函式等等。

## 運算子

* operator!= 測試 allocator、智慧型指標是否不等
* operator== 測試 allocator、智慧型指標是否相等
* operator>= 測試智慧型指標所指向的資料物件位址
* operator< 測試智慧型指標所指向的資料物件位址
* operator<= 測試智慧型指標所指向的資料物件位址
* operator> 測試智慧型指標所指向的資料物件位址
* operator<< 向流物件輸出智慧型指標所指向的資料物件位址

## 智慧型指標

智慧型指標是指當指標物件的生命期結束解構時，同時把指標所指的物件也 delete 或者參照計數減 1。

### unique\_ptr

unique\_ptr 是個類別模板。unique\_ptr 指標物件獨占式地參照所指的資料物件。不能複製構造（copy ctor），也不能複製賦值，也就是對其無法進行複製，不能得到指向同一個物件的兩個 unique\_ptr；但是可以移動構造和移動賦值。當 unique\_ptr 指標物件離開其作用域，生命期結束時，自動使用內部給定的刪除器（deleter）delete 所指向的資料物件。unique\_ptr 十分依賴於右值參照和移動語意。

unique\_ptr 的主要用途有：

1. 實現資源取得即初始化（RAII）。當程式發生異常時，例外處理機制的棧展開（stack unwinding）會銷毀局部變數，包括 unique\_ptr 物件，從而自動釋放資源。
2. 實現右值移動語意。例如，unique\_ptr 類型可作為函式的返回值類型，採用了隱式地移動賦值。
3. 在容器中儲存 unique\_ptr 物件。需要顯式地移動賦值，即通過 std::move ()。

### shared\_ptr

shared\_ptr 是一個類別模板，包裝了使用參照計數的智慧型指標。當 shared\_ptr 在登出其作用域、生命期結束時，解構函式或自動對所指記憶體資料物件的參照計數減去 1。如果記憶體資料物件的參照計數為 0，則會被自動 delete。指向同一資源的 shared\_ptr 共同擁有一個控制塊，其中儲存了 shared\_ptr 的參照計數、指向這一資源的 weak\_ptr 的數目、資源的解構器（deleter ）位址、對該控制塊的可客製化的 allocator。

### weak\_ptr

弱指標 weak\_ptr 用於指向、但不擁有（即不參加參照計數）由 shared\_ptr 管理的資源。弱指標並不直接提供對資源的存取。應該通過 shared\_ptr 來存取資源。當資源的參照計數為 0（所有擁有該資源的 shared\_ptr 物件都已被銷毀），該資源將被 delete，這時如果仍然有弱指標指向該資源，則該弱指標將變為 expired 狀態。這實際上避免了資源的迴圈參照計數。對於迴圈的參照計數，如果把其中的一個 shared\_ptr 的指標指向改為 weak\_ptr 的指標指向，即打破了迴圈參照。

### auto\_ptr

auto\_ptr，即自動指標，在 C++11 標準是過時的類別模板。

## 其他 Helper 類

### default\_delete

作為使用 `operator new` 分配記憶體的 unique\_ptr 物件的 deleter。有兩種形式：

* 為指標： template<class T> struct default\_delete; 函式呼叫成員運算子呼叫 delete
* 為指標陣列： template<class T> struct default\_delete<T []>; 函式呼叫成員運算子模板呼叫 delete []

範例：

### allocator

allocator 是 STL 中非常常用的類別模板，用於客製化記憶體的分配、釋放、管理。目的是封裝 STL 容器在記憶體管理上的低層細節，所以使用者程式不應該直接呼叫 allocator 去管理記憶體，除非是正在客製化容器。allocator 將記憶體的分配與物件的構造初始化解耦，分別用 allocate、construct 兩個成員函式完成；同樣將記憶體的釋放與物件的解構銷毀解耦，分別用 deallocat、destroy 兩個成員函式完成。自訂的 allocator 的實現，必須滿足 C++11 標準的 17.6.3.5 節中的「Table 28 —— Allocator requirements」，簡單說就是要在類中定義若干類型名、模板成員名、成員函式、運算子函式。`<memory>` 中定義的 allocator 類別模板做一些使用全域 malloc/free 函式表達式的記憶體的分配、釋放的平常操作：

### allocator\_traits

allocator\_traits 是對 allocator 的包裝。

### bad\_weak\_ptr

bad\_weak\_ptr 是個異常類。在用弱指標拷貝構造共享指標時，如果弱指標是 expired，那麼拷貝建構函式丟擲 bad\_weak\_ptr 異常。

### enable\_shared\_from\_this

enable\_shared\_from\_this 是一個 helper 類別模板，用於作為資料類的基礎類別。當資料類的物件已經被 shared\_ptr 所擁有，如果需要從該資料類物件獲得其 shared\_ptr 指標，就需要呼叫作為基礎類別的 enable\_shared\_from\_this 的成員函式 `shared_from_this`。例如：

在上例中，b 與 a 共同擁有對資料類物件的同一套參照計數，因此是正確的。如果如此構造 b：

　
: `shared_ptr b( &r);`

則對同一個資料類物件，弄出了兩套參照計數。這會導致當一套參照計數變為 0 而銷毀資料類物件時，另外一套參照計數的 shared\_ptr 成為「懸空」的錯誤。

Microsoft Visual C++ 2010 是在 enable\_shared\_from\_this 類別模板中定義一個資料類型 `_EStype`。shared\_ptr 的物件在呼叫建構函式時，最後將呼叫一個函式 \_Enable\_shared (\_Ty \*\_Ptr, \_Ref\_count\_base \*\_Refptr），其中第一個參數是資料類物件的指標，第二個參數是參照計數控制塊的指標。利用模板元程式設計的參數推導的特性，編譯器選擇 \_Enable\_shared，或是匹配下述模板函式

　
: `template<class _Ty> inline void _Enable_shared(_Ty *_Ptr, _Ref_count_base *_Refptr, typename _Ty::_EStype * = 0)`

或是匹配下述普通函式

　
: `inline void _Enable_shared(const volatile void *, const volatile void *) { }`

前者把 enable\_shared\_from\_this 類中把一個弱指標指向了 shared\_ptr 的參照計數控制塊。從而實現了 enable\_shared\_from\_this 類的語意。

### pointer\_traits

pointer\_traits 是一個類別模板，描述指標類型所需要的一些類型的定義。

* 成員類型
  * pointer 指標類型
  * element\_type 指標的基本類型
  * difference\_type 一般是 std::ptrdiff\_t
  * rebind 成員類別模板，指向需要繫結的其它類型
* 靜態成員函式
  * pointer\_to 從參照類型返回其相應的指標類型，一般為 std::addressof (r)

### raw\_storage\_iterator

raw\_storage\_iterator 是個類別模板，用於表示指向記憶體的一個前向迭代器。其設定運算子在當前所指向記憶體上呼叫值類型的建構函式，生成一個物件。

## 函式

* void\* align (std::size\_t alignment,std::size\_t size,void\*& ptr,std::size\_t& space); 在一塊記憶體（ptr 所指）中，按照對齊要求 slignment，找到一塊長度為 size 的，並重設 ptr 為該記憶體起始位址，space 為該記憶體長度。
* allocate\_shared 這是 shared\_ptr 的工廠函式，指定記憶體分配器 allocator 與資料物件的初始化參數，建立資料物件及 shared\_ptr。
* const\_pointer\_cast 用於 shared\_ptr 物件的 const 的類型轉換。
* declare\_no\_pointers 用於垃圾回收。告知垃圾收集器在指定的記憶體範圍內不含可跟蹤的指標。
* declare\_reachable 用於垃圾回收。令垃圾收集器知道參數指標是指向一塊聲明為可達的動態分配記憶體。聲明為可達的（declared reachable）完全物件，是指該物件作為形參所指，declare\_reachable 呼叫的次數超過 undeclare\_reachable 呼叫次數。聲明為可達的一塊動態分配記憶體不能被垃圾收集器釋放，即使它看起來沒有可達的存取方法。
* dynamic\_pointer\_cast 對 shared\_ptr 物件的執行時動態轉換類型。
* get\_deleter 返回 shared\_ptr 物件的 deleter。
* get\_pointer\_safety 用於垃圾回收。返回當前的垃圾回收器採用的指標類型。
* get\_temporary\_buffer 對給定類型、給定物件數目，分配一塊臨時的記憶體儲存。
* make\_shared 這是 shared\_ptr 的工廠函式，使用給定的資料物件的初始化參數，建立資料物件及 shared\_ptr。
* owner\_less 混合比較 shared\_ptr 物件與 weak\_ptr 物件的序關係。
* pointer\_safety 用於垃圾回收。是 `get_pointer_safety` 的返回值的列舉類型。
* return\_temporary\_buffer 釋放由 `get_temporary_buffer` 分配的臨時性的記憶體塊。
* static\_pointer\_cast 編譯時靜態轉換 shared\_ptr 的值類型。
* swap 交換兩個 shared\_ptr 物件或交換兩個 weak\_ptr 物件.
* undeclare\_no\_pointers 用於垃圾回收。告訴垃圾回收器，在指定範圍的記憶體中，存著可跟蹤指標。
* undeclare\_reachable 用於垃圾回收。告訴垃圾回收器，被形參指標所指的動態分配記憶體塊，復原一次 declare\_reachable 操作。如果動態分配記憶體塊上沒有 declare\_reachable 操作，則垃圾收集器如果判斷它已經是不可達的狀態（即沒有手段存取這塊記憶體）就可把它當垃圾回收。
* uninitialized\_copy 把輸入 iterator 所給出的一個記憶體範圍內的物件陣列的每個元素，逐個拷貝構造到前向的目標 iterator 所指的記憶體上。
* uninitialized\_copy\_n 把輸入 iterator 所給出的一個記憶體位址開始的物件陣列的 n 個元素，逐個拷貝構造到前向的目標 iterator 所指的記憶體上。
* uninitialized\_fill 在輸入 iterator 所給出的一個記憶體範圍內，用給定的物件去拷貝構造多個物件。
* uninitialized\_fill\_n 在輸入 iterator 所給出的一個記憶體開始位址處，用給定的物件去拷貝構造指定的 n 個物件。

## 例子

### 共享指標

### 弱指標例子

### 獨占指標的例子

## 參考文獻

* 《Boost 程式庫完全開發指南 —— 深入 C++「准」標準庫》第 3 章 記憶體管理 3.4 shared\_ptr 作者：羅劍鋒 電子工業出版社 2010 年 9 月版 ISBN 9787121115776