auto_ptr解析

轉自 http://www.cppblog.com/SmartPtr/archive/2007/07/05/27549.html
auto_ptr解析



auto_ptr是當前C++標準庫中提供的一種智能指針,或許相對于boost庫提供的一系列眼花繚亂的智能指

針, 或許相對于Loki中那個無所不包的智能指針,這個不怎么智能的智能指針難免會黯然失色。誠然,

auto_ptr有這樣那樣的不如人意,以至于程序員必須像使用”裸“指針那樣非常小心的使用它才能保證不出

錯,以至于它甚至無法適用于同是標準庫中的那么多的容器和一些算法,但即使如此,我們仍然不能否認這個小小的auto_ptr所蘊含的價值與理念。


  auto_ptr的出現,主要是為了解決“被異常拋出時發生資源泄漏”的問題。即如果我們讓資源在局部對

象構造時分配,在局部對象析構時釋放。這樣即使在函數執行過程時發生異常退出,也會因為異常能保證

局部對象被析構從而保證資源被釋放。auto_ptr就是基于這個理念而設計, 這最早出現在C++之父

Bjarne Stroustrup的兩本巨著TC++PL和D&E中,其主題為"resource acquisition is initialization"(raii,資源獲

取即初始化),然后又在Scott Meyer的<<More Effective C++>>中相關章節的推動下,被加入了C++標準

庫。
  下面我就列出auto_ptr的源代碼,并詳細講解每一部分。因為標準庫中的代碼要考慮不同編譯器支持

標準的不同而插入了不少預編譯判斷,而且命名可讀性不是很強(即使是侯捷老師推薦的SGI版本的stl,可

讀性也不盡如人意), 這里我用了Nicolai M. Josuttis(<<The C++ standard library>>作者)寫的一個

auto_ptr的版本,并做了少許格式上的修改以易于分析閱讀。

?

namespace?std
{
?template<class?T>
?
class?auto_ptr?
?{
?
private:
??T*?ap;?
?
public:

??
//?constructor?&?destructor?-----------------------------------?(1)
??explicit?auto_ptr?(T*?ptr?=?0)?throw()?:?ap(ptr){}

??
~auto_ptr()?throw()?
??{
???delete?ap;
??}

??
??
//?Copy?&?assignment?--------------------------------------------(2)
??auto_ptr?(auto_ptr&?rhs)?throw()?:ap(rhs.release())?{}
??template<class?Y>??
??auto_ptr?(auto_ptr<Y>&?rhs)?throw()?:?ap(rhs.release())?{?}

??auto_ptr&?operator=?(auto_ptr&?rhs)?throw()?
??{
???reset(rhs.release());
???
return?*this;
??}
??template<class?Y>
??auto_ptr&?operator=?(auto_ptr<Y>&?rhs)?throw()?
??{
???reset(rhs.release());
???
return?*this;
??}

??
//?Dereference----------------------------------------------------(3)
??T&?operator*()?const?throw()?
??{
???
return?*ap;
??}
??T*?operator->()?const?throw()?
??{
???
return?ap;
??}

??
//?Helper?functions------------------------------------------------(4)
??
//?value?access
??T*?get()?const?throw()?
??{
???
return?ap;
??}

??
//?release?ownership
??T*?release()?throw()
??{
???T*?tmp(ap);
???ap?=?0;
???
return?tmp;
??}

??
//?reset?value
??void?reset?(T*?ptr=0)?throw()?
??{
???
if?(ap?!=?ptr)?
???{
????delete?ap;
????ap?=?ptr;
???}
??}

??
//?Special?conversions-----------------------------------------------(5)
??template<class?Y>
??
struct?auto_ptr_ref
??{
???Y*?yp;
???auto_ptr_ref?(Y*?rhs)?:?yp(rhs)?{}
??};

??auto_ptr(auto_ptr_ref<T>?rhs)?throw()?:?ap(rhs.yp)?{?}
??auto_ptr&?operator=?(auto_ptr_ref<T>?rhs)?throw()?
??{??
???reset(rhs.yp);
???
return?*this;
??}
??template<class?Y>
??
operator?auto_ptr_ref<Y>()?throw()?
??{
???
return?auto_ptr_ref<Y>(release());
??}
??template<class?Y>
??
operator?auto_ptr<Y>()?throw()
??{
???
return?auto_ptr<Y>(release());
??}
?};
}


1 構造函數與析構函數
auto_ptr在構造時獲取對某個對象的所有權(ownership),在析構時釋放該對象。我們可以這樣使用

auto_ptr來提高代碼安全性:


int* p = new int(0);


auto_ptr<int> ap(p);


從此我們不必關心應該何時釋放p, 也不用擔心發生異常會有內存泄漏。


這里我們有幾點要注意:


1) 因為auto_ptr析構的時候肯定會刪除他所擁有的那個對象,所有我們就要注意了,一個蘿卜一個坑,兩

個auto_ptr不能同時擁有同一個對象。像這樣:


int* p = new int(0);


auto_ptr<int> ap1(p);


auto_ptr<int> ap2(p);


因為ap1與ap2都認為指針p是歸它管的,在析構時都試圖刪除p, 兩次刪除同一個對象的行為在C++標準

中是未定義的。所以我們必須防止這樣使用auto_ptr.


2) 考慮下面這種用法:


int* pa = new int[10];


auto_ptr<int> ap(pa);


因為auto_ptr的析構函數中刪除指針用的是delete,而不是delete [],所以我們不應該用auto_ptr來管理一個

數組指針。


3) 構造函數的explicit關鍵詞有效阻止從一個“裸”指針隱式轉換成auto_ptr類型。


4) 因為C++保證刪除一個空指針是安全的, 所以我們沒有必要把析構函數寫成:


~auto_ptr() throw()?
{
?if(ap) delete ap;
}

?

2 拷貝構造與賦值
與引用計數型智能指針不同的,auto_ptr要求其對“裸”指針的完全占有性。也就是說一個”裸“指針不能同

時被兩個以上的auto_ptr所擁有。那么,在拷貝構造或賦值操作時,我們必須作特殊的處理來保證這個特

性。auto_ptr的做法是“所有權轉移”,即拷貝或賦值的源對象將失去對“裸”指針的所有權,所以,與一般

拷貝構造函數,賦值函數不同, auto_ptr的拷貝構造函數,賦值函數的參數為引用而不是常引用(const

reference).當然,一個auto_ptr也不能同時擁有兩個以上的“裸”指針,所以,拷貝或賦值的目標對象將先

釋放其原來所擁有的對象。


這里的注意點是:


1) 因為一個auto_ptr被拷貝或被賦值后, 其已經失去對原對象的所有權,這個時候,對這個auto_ptr的

提領(dereference)操作是不安全的。如下:


int* p = new int(0);


auto_ptr<int> ap1(p);


auto_ptr<int> ap2 = ap1;


cout<<*ap1;?//錯誤,此時ap1只剩一個null指針在手了


這種情況較為隱蔽的情形出現在將auto_ptr作為函數參數按值傳遞,因為在函數調用過程中在函數的作用

域中會產生一個局部對象來接收傳入的auto_ptr(拷貝構造),這樣,傳入的實參auto_ptr就失去了其對原

對象的所有權,而該對象會在函數退出時被局部auto_ptr刪除。如下:


void f(auto_ptr<int> ap){cout<<*ap;}


auto_ptr<int> ap1(new int(0));


f(ap1);
cout<<*ap1; //錯誤,經過f(ap1)函數調用,ap1已經不再擁有任何對象了。


因為這種情況太隱蔽,太容易出錯了, 所以auto_ptr作為函數參數按值傳遞是一定要避免的。或許大家

會想到用auto_ptr的指針或引用作為函數參數或許可以,但是仔細想想,我們并不知道在函數中對傳入的

auto_ptr做了什么, 如果當中某些操作使其失去了對對象的所有權, 那么這還是可能會導致致命的執

行期錯誤。 也許,用const reference的形式來傳遞auto_ptr會是一個不錯的選擇。

?

2)我們可以看到拷貝構造函數與賦值函數都提供了一個成員模板在不覆蓋“正統”版本的情況下實現

auto_ptr的隱式轉換。如我們有以下兩個類


class base{};


class derived: public base{};


那么下列代碼就可以通過,實現從auto_ptr<derived>到auto_ptr<base>的隱式轉換,因為derived*可以轉

換成base*類型


auto_ptr<base> apbase = auto_ptr<derived>(new derived);

?

3) 因為auto_ptr不具有值語義(value semantic), 所以auto_ptr不能被用在stl標準容器中。


所謂值語義,是指符合以下條件的類型(假設有類A):


A a1;


A a2(a1);


A a3;


a3 = a1;


那么


a2 == a1, a3 == a1


很明顯,auto_ptr不符合上述條件,而我們知道stl標準容器要用到大量的拷貝賦值操作,并且假設其操作

的類型必須符合以上條件。


3 提領操作(dereference)


提領操作有兩個操作, 一個是返回其所擁有的對象的引用, 另一個是則實現了通過auto_ptr調用其所

擁有的對象的成員。如:


struct A
{?
?void f();
}
auto_ptr<A> apa(new A);


(*apa).f();


apa->f();


當然, 我們首先要確保這個智能指針確實擁有某個對象,否則,這個操作的行為即對空指針的提領是未

定義的。

?

4 輔助函數
1) get用來顯式的返回auto_ptr所擁有的對象指針。我們可以發現,標準庫提供的auto_ptr既不提供從“裸”

指針到auto_ptr的隱式轉換(構造函數為explicit),也不提供從auto_ptr到“裸”指針的隱式轉換,從使用上來

講可能不那么的靈活, 考慮到其所帶來的安全性還是值得的。


2) release,用來轉移所有權


3) reset,用來接收所有權,如果接收所有權的auto_ptr如果已經擁有某對象, 必須先釋放該對象。

?

5 特殊轉換
這里提供一個輔助類auto_ptr_ref來做特殊的轉換,按照標準的解釋, 這個類及下面4個函數的作用

是:使我們得以拷貝和賦值non-const auto_ptrs, 卻不能拷貝和賦值const auto_ptrs. 我無法非常準確

的理解這兩句話的意義,但根據我們觀察與試驗,應該可以這樣去理解:沒有這些代碼,我們本來就可以

拷貝和賦值non-const的auto_ptr和禁止拷貝和賦值const的auto_ptr的功能, 只是無法拷貝和賦值臨時

的auto_ptr(右值), 而這些輔助代碼提供某些轉換,使我們可以拷貝和賦值臨時的auto_ptr,但并沒有

使const的auto_ptr也能被拷貝和賦值。如下:


auto_ptr<int> ap1 = auto_ptr<int>(new int(0));


auto_ptr<int>(new int(0))是一個臨時對象,一個右值,一般的拷貝構造函數當然能拷貝右值,因為其參

數類別必須為一個const reference, 但是我們知道,auto_ptr的拷貝函數其參數類型為reference,所以

,為了使這行代碼能通過,我們引入auto_ptr_ref來實現從右值向左值的轉換。其過程為:


1) ap1要通過拷貝 auto_ptr<int>(new int(0))來構造自己


2) auto_ptr<int>(new int(0))作為右值與現有的兩個拷貝構造函數參數類型都無法匹配,也無法轉換成該

種參數類型


3) 發現輔助的拷貝構造函數auto_ptr(auto_ptr_ref<T> rhs) throw()


4) 試圖將auto_ptr<int>(new int(0))轉換成auto_ptr_ref<T>


5) 發現類型轉換函數operator auto_ptr_ref<Y>() throw(), 轉換成功,從而拷貝成功。


從而通過一個間接類成功的實現了拷貝構造右值(臨時對象)


同時,這個輔助方法不會使const auto_ptr被拷貝, 原因是在第5步, 此類型轉換函數為non-const

的,我們知道,const對象是無法調用non-const成員的, 所以轉換失敗。當然, 這里有一個問題要注

意, 假設你把這些輔助轉換的代碼注釋掉,該行代碼還是可能成功編譯,這是為什么呢?debug一下,

?

 我們可以發現只調用了一次構造函數,而拷貝構造函數并沒有被調用,原因在于編譯器將代碼優化掉

了。這種類型優化叫做returned value optimization,它可以有效防止一些無意義的臨時對象的構造。當

然,前提是你的編譯器要支持returned value optimization。

  可見,auto_ptr短短百來行的代碼,還是包含了不少"玄機"的。?

轉載于:https://www.cnblogs.com/XiHua/p/3711025.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/273763.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/273763.shtml
英文地址,請注明出處:http://en.pswp.cn/news/273763.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring——自定義屬性編輯器+Bean的生存范圍+Bean的生命周期

一、自定義屬性編輯器&#xff08;一個類&#xff09;&#xff1a; 步驟&#xff1a; 1.寫一個類&#xff0c;這個類繼承PropertyEditorSupport。 2.重寫setAsText()方法。 3.在bean.xml文件中添加屬性編輯器的bean。 例子&#xff1a;點擊打開鏈接 二、Bean的生存范圍&#xf…

數據庫---增刪改查

1、添加數據INSERT INTO 表名(字段名1,字段名2...) values(值1,值2...); INSERT INTO newstu(id,name)values(12,胡歌);INSERT INTO 表名 values(值1&#xff0c;值2); INSERT INTO newstu values(12,胡歌); 注意&#xff1a; 值與字段必須一一對應&#xff0c;且個數相同&…

數據庫---查詢(詳細)

1、查詢 SELECT * FROM 表名 WHERE 條件&#xff1b; (1)、SELECT * FROM 表名&#xff1b;#查詢表內的所有數據 SELECT * FROM PRODUCT;(2)、SELECT 字段1&#xff0c;字段2... FROM 表名;#查詢表內的指定字段的數據 SELECT PIN,PRICE FROM PRODUCT; (3)、SELECT * FRO…

微信公眾平臺開發接口PHP SDK完整版

官方提供的SDK只有一個文本消息功能&#xff0c;我們將所有消息的消息類型及事件響應都整理了進來&#xff0c;并且加入日志記錄&#xff0c;代碼如下&#xff1a; 更新日志&#xff1a; 2013-01-01 版本1.02014-03-15 增加圖片、視頻、語音的內容回復2014-04-09 增加菜單鏈接事…

BZOJ3570 : DZY Loves Physics I

考慮兩個質量均為m&#xff0c;速度分別v1、v2的小球發生完全彈性碰撞的影響&#xff1a; 由動能守恒得&#xff1a; $\frac{1}{2}mv_1^2\frac{1}{2}mv_2^2\frac{1}{2}mv_1^2\frac{1}{2}mv_2^2$$v_1^2v_2^2v_1^2v_2^2$ 由動量守恒得&#xff1a; $mv_1mv_2mv_1mv_2$$v_1v_2v_1v…

數據庫---主鍵約束

1、設置主鍵約束(1)、方式一&#xff1a;創建表時&#xff0c;在字段在描述處聲明指定字段為主鍵&#xff1b; CREATE TABLE 表名(字段 類型(長度) PRIMARY KEY,字段 類型(長度) );CREATE TABLE STUDENT(STU_ID INT PAIMARY KEY,STU_NAME VARCHAR(255) );(2)、方式二&#xff1…

關于VS2010幫助文檔的使用和VC6.0在Win7 64位下的使用

由于購置了新的電腦&#xff0c;安裝的是Win7 64位的操作系統&#xff0c;這兩天我在重新安裝編程環境的時候遇到一些問題&#xff0c;現在都解決掉了&#xff0c;分享出來以供需要的人參考。 一、以前使用的是VS2008&#xff0c;從VC6到2008這么多年了一只使用的MSDN是帶索引的…

數據庫---聚合查詢

聚合查詢&#xff1a;縱向查詢&#xff0c;它是對一列的值進行計算&#xff0c;然后返回一個單一的值&#xff1b;另外聚合查詢是忽略空值。 ?count&#xff1a;統計指定列不為NULL的記錄行數&#xff1b;?sum&#xff1a;計算指定列的數值和&#xff0c;如果指定列類型不是數…

【記憶法】心智繪圖

心智繪圖方法 1.提出具體、明確的記憶任務(以30min為單位) 記憶25min休息5min2.及時復習&#xff0c;減少遺忘(記憶關鍵字) 看到關鍵詞能夠回想起全部的內容。看到關鍵詞能夠產生生動的圖像。3.平時多背誦 有時間多記一些小東西、小片段4.復述和再現 聽到或看到什么好的故事要及…

數據庫---分組查詢

一、分組查詢&#xff1a;指使用group by字句對查詢信息進行分組。格式&#xff1a; SELECT 字段1,字段2... FROM 表名 GROUP BY 分組字段 HAVING 分組條件; 分組操作中的having子語句&#xff0c;是用于在分組后對數據進行過濾的&#xff0c;作用類似于where條件。 1、having與…

centos安裝coreseek

安裝依賴 yum install make gcc g gcc-c libtool autoconf automake imake mysql-devel libxml2-devel expat-devel 下載coreseek 4.1 $ wget http://www.coreseek.cn/uploads/csft/4.0/coreseek-4.1-beta.tar.gz $ tar xzvf coreseek-4.1-beta.tar.gz $ cd coreseek-4.1-beta…

HTML---HTML簡介

1、HTML簡介&#xff1a;*什么事HTML&#xff1f; -HypperText Markup Language&#xff1a;超文本標記語言&#xff0c;網頁語言。**超文本&#xff1a;超出文本的范疇&#xff0c;使用HTML可以輕松實現簡單操作。**標記&#xff1a;HTML所有的操作都是通過標記實現的&…

谷歌Android各版本的代號變遷

簡單回顧下Android發展歷程2003年10月&#xff0c;Andy Rubin&#xff08;安迪魯賓&#xff09;等人創建Android公司&#xff0c;并組建Android團隊。2005年8月17日&#xff0c;Google低調收購了成立僅22個月的高科技企業Android及其團隊。安迪魯賓成為Google公司工程部副總裁&…

HTMLL---HTML中常用標簽(文字、注釋標簽)

1、文字標簽和注釋標簽*文字標簽和注釋標簽- <font></font>-屬性* size:文字的大小&#xff0c;取值范圍1-7&#xff0c;超出7默認為7* color:文字的顏色-兩種表示方式**英文單詞&#xff1a; red, green, blue, black, white, yellow, gray**使用十六進制數表示&a…

Map.Entry

如何簡便的遍歷Map 你是否已經對每次從Map中取得關鍵字然后再取得相應的值感覺厭倦&#xff1f; 使用JDK5的增強for循環&#xff0c;來遍歷Map,簡單多了&#xff0c;比Map.Entry還方便。 看代碼&#xff1a; Java代碼 for (String key : map.keySet()) { System.out.pri…

HTML---HTML中常用的標簽(標題,水平,特殊標簽)

1、標題標簽、水平標簽和特殊字符*標題標簽- <h1>... </h1>、 <h2>... </h2>、 <h3>... </h3>、... <h6>... </h6>-特點&#xff1a;從h1到h6字體由大到小、同時 自動換行。*水平標簽- <hr/>-屬性** size&#xff1a;水…

圖解SQL的inner join(join)、left join、right join、full outer join、union、union all的區別...

對于SQL的Join&#xff0c;在學習起來可能是比較亂的。我們知道&#xff0c;SQL的Join語法有很多inner的&#xff0c;有outer的&#xff0c;有left的&#xff0c;有時候&#xff0c;對于Select出來的結果集是什么樣子有點不是很清楚。Coding Horror上有一篇文章,通過文氏圖 Ven…

數據庫---四中連接查詢(交叉、左連接、右連接、完整查詢)

個人博客 &#xff1a;https://www.siyuan.run CSDN&#xff1a;https://blog.csdn.net/siyuan 微信小程序&#xff1a;思遠Y 1、交叉連接查詢 : (基本不適用---得到的是兩張表數據的乘積) 語法&#xff1a;SELECT * FROM 表1,表2; PS&#xff1a;與表關系無關 示例&#xff…

如何用C#語言構造蜘蛛程序

"蜘蛛"&#xff08;Spider&#xff09;是Internet上一種很有用的程序&#xff0c;搜索引擎利用蜘蛛程序將Web頁面收集到數據庫&#xff0c;企業利用蜘蛛程序監視競爭對手的網站并跟蹤變動&#xff0c;個人用戶用蜘蛛程序下載Web頁面以便脫機使用&#xff0c;開發者利…

數據庫---練習題(45道)

準備工作 CREATE DATABASE STUDENTS; CREATE TABLE STUDENT( SNO VARCHAR(32) PRIMARY KEY NOT NULL, SNAME VARCHAR(32) NOT NULL, SSEX VARCHAR(32) NOT NULL, SBIRTHDAY DATETIME, CLASS VARCHAR(20) ); CREATE TABLE COURSE( CNO VARCHAR(20) PRIMARY KEY NOT NULL, CNAM…