国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

STL中的智能指針(Smart Pointer)及其源碼剖析: std::auto_ptr

2019-11-08 02:00:56
字體:
來源:轉載
供稿:網友

STL中的智能指針(Smart Pointer)及其源碼剖析: std::auto_ptr

auto_ptr是STL中的智能指針家族的成員之一, 它管理由new exPRession獲得的對象,在auto_ptr對象銷毀時,他所管理的對象也會自動被delete` 掉。auto_ptr的拷貝構造函數和拷貝賦值會改變 right hand value,并且拷貝的副本不會等于原始的、被拷貝的那個auto_ptr對象的值。(實際上,auto_ptr` 的拷貝構造函數和拷貝賦值函數會讓 left hand value 接管 right hand value 所管理的對象。)由于不一樣的拷貝語義, auto_ptr 不適用于標準容器, 因此,更建議使用std::unique_ptr。

一. auto_ptr 的使用

1. auto_ptr 的聲明

//(until C++17) //(deprecated since C++11) template<class T> class auto_ptr; template<> class auto_ptr<void>;

2. auto_ptr 的構造函數

//deprecated... explicit auto_ptr(X* p = 0); (1) auto_ptr(auto_ptr& r); (2) template<class Y> (3) auto_ptr<auto_ptr<Y>& r); template<class Y> (4) auto_ptr(auto_ptr_ref<Y> m);

(1) 構造 auto_ptr 對象, 讓它管理 p 指向的對象。 (2) 構造 auto_ptr 對象,讓它接管 r 管理的對象。實際上新的 auto_ptr 對象是靠 r.release() 函數獲得管理權的。因此,r 失去了管理權。 (3) 這個構造函數和 (2) 類似, 主要針對能隱式轉換為 T* 類型的 Y*。 (4) 構造 auto_ptr 對象, 讓它接管 auto_ptr_ref<Y> 類型的 m 管理的對象。而m 是通過 p.release()auto_ptr 對象 p 中獲取管理權的。

Q: what is auto_ptr_ref, what it achieves and how it achieves it ?

A: It is rather confusing. Basically, auto_ptr_ref exists because the auto_ptr copy constructor isn’t really a copy constructor in the standard sense of the Word.

Copy constructors typically have a signature that looks like this:

X(const X &b); The auto_ptr copy constructor has a signature that looks like this:

X(X &b) This is because auto_ptr needs to modify the object being copied from in order to set its pointer to 0 to facilitate the ownership semantics of auto_ptr.

Sometimes, temporaries cannot match a copy constructor that doesn’t declare its argument const. This is where auto_ptr_ref comes in. The compiler won’t be able to call the non-const version of the copy constructor, but it can call the conversion Operator. The conversion operator creates an auto_ptr_ref object that’s just sort of a temporary holder for the pointer. The auto_ptr constructor or operator = is called with the auto_ptr_ref argument.

If you notice, the conversion operator in auto_ptr that automatically converts to an auto_ptr_ref does a release on the source auto_ptr, just like the copy constructor does.

It’s kind of a weird little dance that happens behind the scenes because auto_ptr modifies the thing being copied from.

簡單地總結: auto_ptr_ref 主要解決用右值來構造 auto_ptr 的情況。 因為, auto_ptr(auto_ptr& r) 構造函數只能以左值引用做參數。當右值來構造 auto_ptr_ref 的時候,實際上實現過程如下(這其實是移動語義的早期實現版本): SmartPtr_auto_ptr

3. auto_ptr的析構函數: 銷毀管理的對象。

~auto_ptr(); // deprecated

4. 拷貝賦值函數

//deprecated auto_ptr& operator=(auto_ptr& r); (1) template<class Y> (2) auto_ptr& operator=(auto_ptr<Y>& r); auto_ptr& operator=(auto_ptr_ref m); (3)

auto_ptr 的拷貝賦值函數會讓 left hand value 接管 right hand value 所管理的對象。

5. 隱式類型轉換函數

//deprecated... template<class Y> (1) operator auto_ptr_ref<Y>(); template<class Y> (2) operator auto_ptr<Y>();

(1) 將該對象隱式轉換為 auto_ptr_ref<Y> 類型。 (2) 將該對象隱式轉換為 auto_ptr<Y> 類型。

6. 其他函數(auto_ptr::get, auto_ptr::operator*、auto_ptr::operator->, auto_ptr::reset, auto_ptr::release)

//deprecated... T* get() const; (1) T& operator*() const; (2) T* operator->() const; (3) void reset(T* p = 0); (4) T* release(); (5)

(1) 返回該 *this 所管理對象的指針。 (2) 返回該 *this 所管理對象。 (3) 返回該 *this 所管理對象的指針。 (4) 讓 *this 管理 p 所指向的對象,如果 *this 已有管理的對象,則先 delete 掉當前管理的對象。 (5) 移交出 *this 所管理對象的管理權。返回 *this 所管理對象的指針,并將 *this 內部的指針置為空。

7. 例子

代碼

#include <iostream>#include <string>#include <memory>using namespace std;// 展示測試結果 template<class Ty>void Test(auto_ptr<Ty>& showPtr, string name, string hint){ cout << hint; if(showPtr.get() == nullptr) cout << name << ".get() == nullptr" << endl; else cout << "*" << name << ".get() == " << *showPtr.get() << endl; }// for test...class Base{public: Base(double pi = 0.0) : m_pi(pi){ //... } virtual void ShowName() const { cout << "Base Object"; } double m_pi;};class Derive : public Base{public: virtual void ShowName() const { cout << "Derive Object"; }};ostream& operator<<(ostream& os, const Base& b){ b.ShowName(); return os;}ostream& operator<<(ostream& os, const Derive& b){ b.ShowName(); return os;}int main(){ // 構造函數... // explicit auto_ptr(X* p = 0); (1) auto_ptr<int> intPtr1; Test(intPtr1, "intPtr1", "explicit auto_ptr(X* p = 0).../n"); int* ptr = new int(2); auto_ptr<int> intPtr2(ptr); Test(intPtr2, "intPtr2", ""); Derive* dp = new Derive; auto_ptr<Derive> dptr(dp); Test(dptr, "dptr", ""); cout << endl; // auto_ptr(auto_ptr& r); (2) auto_ptr<int> intPtr3(intPtr2); Test(intPtr3, "intPtr3", "auto_ptr(auto_ptr& r).../n"); Test(intPtr2, "intPtr2", "intPtr2 失去了對 ptr 的控制權: "); cout << endl; // template<class Y> (3) // auto_ptr<auto_ptr<Y>& r); auto_ptr<Base> bPtr(dptr); Test(bPtr, "bPtr", "template<class Y> auto_ptr<auto_ptr<Y>& r).../n"); Test(dptr, "dptr", "dptr 失去了對 dp 的控制權: "); cout << endl; // template<class Y> (4) // auto_ptr(auto_ptr_ref<Y> m); auto_ptr_ref<string> ptrRef(new string("many strings")); auto_ptr<string> strPtr(ptrRef); Test(strPtr, "strPtr", "template<class Y> auto_ptr(auto_ptr_ref<Y> m).../n"); cout << endl; // 拷貝賦值函數 // auto_ptr& operator=(auto_ptr& r); (1) auto_ptr<int> intPtr4; intPtr4 = intPtr3; Test(intPtr4, "intPtr4", "auto_ptr& operator=(auto_ptr& r).../n"); Test(intPtr3, "intPtr3", "intPtr3 失去了對 ptr 的控制權: "); cout << endl; // template<class Y> (2) // auto_ptr& operator=(auto_ptr<Y>& r); auto_ptr<Derive> derivePtr(new Derive); Test(derivePtr, "derivePtr", "template<class Y> auto_ptr& operator=(auto_ptr<Y>& r).../n"); auto_ptr<Base> basePtr; basePtr = derivePtr; Test(basePtr, "basePtr", "basePtr 獲得了控制權....../n"); Test(derivePtr, "derivePtr", "derivePtr 失去了控制權.../n"); cout << endl; // auto_ptr& operator=(auto_ptr_ref m); (3) auto_ptr_ref<string> strPtrRef(new string("auto_ptr_ref strings")); auto_ptr<string> strAutoPtr; strAutoPtr = strPtrRef; Test(strAutoPtr, "strAutoPtr", "auto_ptr& operator=(auto_ptr_ref m).../n"); cout << endl; // 其他函數 // T* get() const; (1) int* pAddr = new int(5); cout << "pAddr = " << pAddr << endl; auto_ptr<int> addr(pAddr); cout << "addr.get() = " << addr.get() << endl; cout << endl; // T& operator*() const; (2) cout << "*pAddr = " << *pAddr << endl; cout << "*addr.get() = " << *addr.get() << endl; cout << endl; // T* operator->() const; (3) Base* pBase = new Base(3.14159); auto_ptr<Base> spBase(pBase); cout << "pBase->m_pi = " << pBase->m_pi << endl; cout << "spBase->m_pi = " << spBase->m_pi << endl; cout << endl; // void reset(T* p = 0); (4) intPtr4.reset(new int(-1)); Test(intPtr4, "intPtr4", "void reset(T* p = 0).../n"); // T* release(); (5) intPtr4.release(); Test(intPtr4, "intPtr4", "T* release().../n"); cout << endl; return 0;}

運行結果:

SmartPtr_auto_ptr_test

二. auto_ptr 源碼剖析(源碼出自 VS2015)

1. 輔助類 auto_ptr_ref 的源碼

template<class _Ty> struct auto_ptr_ref { // proxy reference for auto_ptr copying explicit auto_ptr_ref(_Ty *_Right) : _Ref(_Right) { // construct from generic pointer to auto_ptr ptr } _Ty *_Ref; // generic pointer to auto_ptr ptr };

這個輔助類的源碼比較簡單, 沒有什么說的。前面也分析過了,這個輔助類其實是為了幫助 auto_ptr 完成右值引用傳參而設計的。

2. auto_ptr 構造函數的源碼

typedef auto_ptr<_Ty> _Myt; // 管理類的類型 typedef _Ty element_type; // 被管理元素的類型 explicit auto_ptr(_Ty *_Ptr = 0) (1) : _Myptr(_Ptr) { // construct from object pointer } auto_ptr(_Myt& _Right) (2) : _Myptr(_Right.release()) { // construct by assuming pointer from _Right auto_ptr } template<class _Other> (3) auto_ptr(auto_ptr<_Other>& _Right) : _Myptr(_Right.release()) { // construct by assuming pointer from _Right } auto_ptr(auto_ptr_ref<_Tp> Right) (4) : _Myptr(Right._Ref) {}

其中, auto_ptr 的成員變量 _Ty *_Myptr 指向被它管理的對象。 (1) 從原始指針中獲取控制權。注意,由源碼可知, auto_ptr 并沒有將原始指針的控制權剝奪(從實現來看, 也不能剝奪, 因為 Ptr 不是指針引用,無法更改原始指針的指向),原始指針仍然保有對其資源的控制權。但是,該資源的釋放權實際上已經交給了 auto_ptr 對象。如:

int* ptr = new int(3); auto_ptr<int> autoPtr(ptr); //error: 在 autoPtr 生命期結束后會釋放ptr指向的資源。 //如果在這里釋放資源, 在 autoPtr 生命期結束后就會崩潰。 delete ptr;

(2) 從 auto_ptr 對象中奪取對資源的控制權。由源碼可知, _Right 將不再保有對其資源的控制。注意,這里是左值引用參數,不能接收右值參數。 (3) 與 (2) 類似。是針對可轉換為 _Ty* 類型的 _Other* 類型的構造函數。 (4) 這個構造函數的參數是 auto_ptr_ref<_Ty> 類型的。注意,它不是左值引用類型的參數,因此可以接收右值類型。這也是右值傳參的必經之路。

3. auto_ptr 析構函數的源碼

~auto_ptr() { // destroy the object delete _Myptr; }

4. auto_ptr 拷貝賦值函數的源碼

_Myt& operator=(_Myt& _Right) (1) { // assign compatible _Right (assume pointer) reset(_Right.release()); return (*this); } template<class _Other> (2) _Myt& operator=(auto_ptr<_Other>& _Right) { // assign compatible _Right (assume pointer) reset(_Right.release()); return (*this); } _Myt& operator=(auto_ptr_ref<_Ty> _Right) (3) { // assign compatible _Right._Ref (assume pointer) _Ty *_Ptr = _Right._Ref; _Right._Ref = 0; // release old reset(_Ptr); // set new return (*this); }

這里涉及到解決“自我賦值”(assignment to self)的問題,詳情請參閱《Effective C++》 Item 11: Handle assignment to self in operator=。 (1) 將同類型的 _Right 管理的資源移交給 *this。其中,reset(_Right.release()) 先將 _Right 的資源以返回值的形式移交,然后設置給 *this, 這樣就防止了“自我賦值”的時候出現問題。(如果 *this 就是 _Right, 那么執行完 _Right.release() 后, *this 管理的資源已經以返回值的形式移交出來作為參數,然后又 reset 給了自己)注意,這里是左值引用參數,不能接收右值參數。 (2) 與(1)類似。是針對可轉換為 _Ty* 類型的 _Other* 類型的拷貝函數。 (3) 將一個 auto_ptr_ref 類型的變量賦值給 *this, 實際上是將資源的控制權移交給 *this。這同樣是為了傳右值參數而設計的。這個函數體內冗余的代碼同樣是為了防止 _Right._Ref 等于 _Myptr 的情況。

5. auto_ptr 隱式類型轉換函數

template<class _Other> (1) operator auto_ptr_ref<_Other>() { // convert to compatible auto_ptr_ref _Other *_Cvtptr = _Myptr; // test implicit conversion auto_ptr_ref<_Other> _Ans(_Cvtptr); _Myptr = 0; // pass ownership to auto_ptr_ref return (_Ans); } template<class _Other> (2) operator auto_ptr<_Other>() { // convert to compatible auto_ptr return (auto_ptr<_Other>(*this)); }

(1) auto_ptrauto_ptr_ref 的隱式轉換函數。 由源碼可知,該隱式轉換也會剝奪 *this 對資源的管理權。 這個轉換雖然代碼短小,但是能量巨大, 右值類型的 auto_ptr 作參數傳遞時,全靠這個轉換函數來起到周轉的作用。當然,現在新的C++標準有更好的方法來解決這個問題 —— 右值引用。 (2) 可轉換的類型…

6. auto_ptr 其他函數

_Ty *get() const (1) {// return wrapped pointer return (_Myptr); } _Ty& operator*() const (2) { // return designated value if (_Myptr == 0) _DEBUG_ERROR("auto_ptr not dereferencable"); return (*get()); } _Ty *operator->() const (3) { // return pointer to class object if (_Myptr == 0) _DEBUG_ERROR("auto_ptr not dereferencable"); return (get()); } void reset(_Ty *_Ptr = 0) (4) { // destroy designated object and store new pointer if (_Ptr != _Myptr) delete _Myptr; _Myptr = _Ptr; } _Ty *release() (5) { // return wrapped pointer and give up ownership _Ty *_Tmp = _Myptr; _Myptr = 0; return (_Tmp); }

(1) 獲取 *this 所管理資源的指針, 這個沒什么說的。 (2) 重載 operator*() 操作符,讓 *this 有指針的行為。 (3) 重載 operator->() 操作符,讓 *this 有指針的行為。 (4) 重新設置 *this 管理的資源, 當然在此之前要將 *this 管理的資源釋放掉。類似于 operator= 的檢查,如果_Ptr 指向的資源就是 *this 管理的資源,就忽略這個操作。否則會提前釋放資源。 (5) *this 移交出管理權,并將資源的指針返回。因此需要先記錄下資源的地址,然后將 *this 指向資源的指針置為空,最后返回資源的地址。

三. 總結

auto_ptr 用以 RAII(Resource Acquisition Is Initialization) 思想實現對資源的管理(詳情可參考《Effective C++》Item 13: Use objects to manage resources)。但auto_ptr 屬于該思想實現的早期版本,現在的標準庫已經不推薦使用該工具了。但是,了解auto_ptr 的功能和實現還是有必要的,其一是,它相當于是其它更復雜智能指針的簡化版本,源碼簡單,容易上手,對后面學習其它智能指針做鋪墊; 其二是, 學習 auto_ptr 可以讓那個我們對 RAII 思想有所領悟。

四. 參考文獻

Scott Meyers 著, 侯捷譯《Effective C++》cppreference.comVS2015 相關源碼

五. 推薦閱讀

關于右值: cppreference.com關于值類型的詳細解讀:lvalue,rvalue,xvalue,prvalue,glvalue關于右值引用: C++11移動語義探討——從臨時對象到右值引用
上一篇:Groovy分類注入

下一篇:npm常用命令

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 黄浦区| 连州市| 且末县| 巨野县| 柳江县| 宝清县| 沽源县| 保康县| 河间市| 米泉市| 怀远县| 溆浦县| 茂名市| 田阳县| 调兵山市| 沐川县| 利川市| 盐山县| 浦城县| 杂多县| 洪雅县| 和田县| 凤山县| 绥江县| 读书| 西峡县| 高唐县| 洪雅县| 江达县| 澳门| 内黄县| 南召县| 甘谷县| 盖州市| 花垣县| 日喀则市| 丹凤县| 黄浦区| 玛多县| 景泰县| 临西县|