日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

auto_ptr解析

發(fā)布時間:2023/12/10 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 auto_ptr解析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
轉(zhuǎn)自 http://www.cppblog.com/SmartPtr/archive/2007/07/05/27549.htmlauto_ptr解析



auto_ptr是當(dāng)前C++標(biāo)準(zhǔn)庫中提供的一種智能指針,或許相對于boost庫提供的一系列眼花繚亂的智能指

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

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

錯,以至于它甚至無法適用于同是標(biāo)準(zhǔn)庫中的那么多的容器和一些算法,但即使如此,我們?nèi)匀徊荒芊裾J(rèn)這個小小的auto_ptr所蘊含的價值與理念。


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

象構(gòu)造時分配,在局部對象析構(gòu)時釋放。這樣即使在函數(shù)執(zhí)行過程時發(fā)生異常退出,也會因為異常能保證

局部對象被析構(gòu)從而保證資源被釋放。auto_ptr就是基于這個理念而設(shè)計, 這最早出現(xiàn)在C++之父

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

取即初始化),然后又在Scott Meyer的<<More Effective C++>>中相關(guān)章節(jié)的推動下,被加入了C++標(biāo)準(zhǔn)

庫。
  下面我就列出auto_ptr的源代碼,并詳細(xì)講解每一部分。因為標(biāo)準(zhǔn)庫中的代碼要考慮不同編譯器支持

標(biāo)準(zhǔn)的不同而插入了不少預(yù)編譯判斷,而且命名可讀性不是很強(qiáng)(即使是侯捷老師推薦的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 構(gòu)造函數(shù)與析構(gòu)函數(shù)
auto_ptr在構(gòu)造時獲取對某個對象的所有權(quán)(ownership),在析構(gòu)時釋放該對象。我們可以這樣使用

auto_ptr來提高代碼安全性:


int* p = new int(0);


auto_ptr<int> ap(p);


從此我們不必關(guān)心應(yīng)該何時釋放p, 也不用擔(dān)心發(fā)生異常會有內(nèi)存泄漏。


這里我們有幾點要注意:


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

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


int* p = new int(0);


auto_ptr<int> ap1(p);


auto_ptr<int> ap2(p);


因為ap1與ap2都認(rèn)為指針p是歸它管的,在析構(gòu)時都試圖刪除p, 兩次刪除同一個對象的行為在C++標(biāo)準(zhǔn)

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


2) 考慮下面這種用法:


int* pa = new int[10];


auto_ptr<int> ap(pa);


因為auto_ptr的析構(gòu)函數(shù)中刪除指針用的是delete,而不是delete [],所以我們不應(yīng)該用auto_ptr來管理一個

數(shù)組指針。


3) 構(gòu)造函數(shù)的explicit關(guān)鍵詞有效阻止從一個“裸”指針隱式轉(zhuǎn)換成auto_ptr類型。


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


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

?

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

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

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

拷貝構(gòu)造函數(shù),賦值函數(shù)不同, auto_ptr的拷貝構(gòu)造函數(shù),賦值函數(shù)的參數(shù)為引用而不是常引用(const

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

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


這里的注意點是:


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

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


int* p = new int(0);


auto_ptr<int> ap1(p);


auto_ptr<int> ap2 = ap1;


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


這種情況較為隱蔽的情形出現(xiàn)在將auto_ptr作為函數(shù)參數(shù)按值傳遞,因為在函數(shù)調(diào)用過程中在函數(shù)的作用

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

對象的所有權(quán),而該對象會在函數(shù)退出時被局部auto_ptr刪除。如下:


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


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


f(ap1);
cout<<*ap1; //錯誤,經(jīng)過f(ap1)函數(shù)調(diào)用,ap1已經(jīng)不再擁有任何對象了。


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

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

auto_ptr做了什么, 如果當(dāng)中某些操作使其失去了對對象的所有權(quán), 那么這還是可能會導(dǎo)致致命的執(zhí)

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

?

2)我們可以看到拷貝構(gòu)造函數(shù)與賦值函數(shù)都提供了一個成員模板在不覆蓋“正統(tǒng)”版本的情況下實現(xiàn)

auto_ptr的隱式轉(zhuǎn)換。如我們有以下兩個類


class base{};


class derived: public base{};


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

換成base*類型


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

?

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


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


A a1;


A a2(a1);


A a3;


a3 = a1;


那么


a2 == a1, a3 == a1


很明顯,auto_ptr不符合上述條件,而我們知道stl標(biāo)準(zhǔn)容器要用到大量的拷貝賦值操作,并且假設(shè)其操作

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


3 提領(lǐng)操作(dereference)


提領(lǐng)操作有兩個操作, 一個是返回其所擁有的對象的引用, 另一個是則實現(xiàn)了通過auto_ptr調(diào)用其所

擁有的對象的成員。如:


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


(*apa).f();


apa->f();


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

定義的。

?

4 輔助函數(shù)
1) get用來顯式的返回auto_ptr所擁有的對象指針。我們可以發(fā)現(xiàn),標(biāo)準(zhǔn)庫提供的auto_ptr既不提供從“裸”

指針到auto_ptr的隱式轉(zhuǎn)換(構(gòu)造函數(shù)為explicit),也不提供從auto_ptr到“裸”指針的隱式轉(zhuǎn)換,從使用上來

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


2) release,用來轉(zhuǎn)移所有權(quán)


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

?

5 特殊轉(zhuǎn)換
這里提供一個輔助類auto_ptr_ref來做特殊的轉(zhuǎn)換,按照標(biāo)準(zhǔn)的解釋, 這個類及下面4個函數(shù)的作用

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

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

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

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

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


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


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

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

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


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


2) auto_ptr<int>(new int(0))作為右值與現(xiàn)有的兩個拷貝構(gòu)造函數(shù)參數(shù)類型都無法匹配,也無法轉(zhuǎn)換成該

種參數(shù)類型


3) 發(fā)現(xiàn)輔助的拷貝構(gòu)造函數(shù)auto_ptr(auto_ptr_ref<T> rhs) throw()


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


5) 發(fā)現(xiàn)類型轉(zhuǎn)換函數(shù)operator auto_ptr_ref<Y>() throw(), 轉(zhuǎn)換成功,從而拷貝成功。


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


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

的,我們知道,const對象是無法調(diào)用non-const成員的, 所以轉(zhuǎn)換失敗。當(dāng)然, 這里有一個問題要注

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

?

 我們可以發(fā)現(xiàn)只調(diào)用了一次構(gòu)造函數(shù),而拷貝構(gòu)造函數(shù)并沒有被調(diào)用,原因在于編譯器將代碼優(yōu)化掉

了。這種類型優(yōu)化叫做returned value optimization,它可以有效防止一些無意義的臨時對象的構(gòu)造。當(dāng)

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

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

轉(zhuǎn)載于:https://www.cnblogs.com/XiHua/p/3711025.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的auto_ptr解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。