智能指针——auto_ptr
1. 開篇
C++里面的四個(gè)智能指針:auto_ptr、unique_ptr、shared_ptr、weak_ptr,其中后三個(gè)是C++11支持,而這個(gè)auto_ptr已經(jīng)被C++11棄用。但auto_ptr還是有必要學(xué)習(xí)的。C++的auto_ptr所做的事情,就是動(dòng)態(tài)分配對(duì)象以及當(dāng)對(duì)象不在需要時(shí)自動(dòng)執(zhí)行清理。
2. 構(gòu)造函數(shù)與析構(gòu)函數(shù)
auto_ptr在構(gòu)造時(shí)獲取對(duì)某個(gè)對(duì)象的所有權(quán)(ownership),在析構(gòu)時(shí)釋放該對(duì)象。我們可以這樣使用auto_ptr來提高代碼安全性:
int*p = new int(0); auto_ptr<int> ap(p);從此我們不必關(guān)心應(yīng)該何時(shí)釋放p,也不用擔(dān)心發(fā)生異常會(huì)有內(nèi)存泄漏。
注意:
(1) 因?yàn)閍uto_ptr析構(gòu)的時(shí)候肯定會(huì)刪除他所擁有的那個(gè)對(duì)象,所以我們就要注意了,一個(gè)蘿卜一個(gè)坑,兩個(gè)auto_ptr不能同時(shí)擁有同一個(gè)對(duì)象。像這樣:
因?yàn)閍p1與ap2都認(rèn)為指針p是歸它管的,在析構(gòu)時(shí)都試圖刪除p,兩次刪除同一個(gè)對(duì)象的行為在C++標(biāo)準(zhǔn)中是未定義的。所以我們必須防止這樣使用auto_ptr。
(2) 下面這種用法:
String *sar = new String[10]; auto_ptr<String> ap(sar);因?yàn)閍uto_ptr的析構(gòu)函數(shù)中刪除指針用的是delete,而不是delete[],所以我們不應(yīng)該用auto_ptr來管理一個(gè)數(shù)組指針。
(3) 構(gòu)造函數(shù)的explicit關(guān)鍵詞有效阻止從一個(gè)"裸"指針隱式轉(zhuǎn)換成auto_ptr類型。
3. 拷貝構(gòu)造與賦值
與引用計(jì)數(shù)型智能指針不同的,auto_ptr要求其對(duì)"裸"指針的完全占有性。也就是說一個(gè)"裸"指針不能同時(shí)被兩個(gè)以上的auto_ptr所擁有。那么,在拷貝構(gòu)造或賦值操作時(shí),我們必須作特殊的處理來保證這個(gè)特性。 auto_ptr的做法是"所有權(quán)轉(zhuǎn)移" ,即拷貝或賦值的源對(duì)象將失去對(duì)"裸"指針的所有權(quán),所以,與一般拷貝構(gòu)造函數(shù),賦值函數(shù)不同,auto_ptr的拷貝構(gòu)造函數(shù),賦值函數(shù)的參數(shù)為引用而不是常引用(const reference)。當(dāng)然,一個(gè)auto_ptr也不能同時(shí)擁有兩個(gè)以上的"裸"指針,所以,拷貝或賦值的目標(biāo)對(duì)象將先釋放其原來所擁有的對(duì)象。
注意:
(1) 因?yàn)橐粋€(gè)auto_ptr被拷貝或被賦值后,其已經(jīng)失去對(duì)原對(duì)象的所有權(quán),這個(gè)時(shí)候,對(duì)這個(gè)auto_ptr的提領(lǐng)(dereference)操作是不安全的。
如下:
這種情況較為隱蔽的情形出現(xiàn)在將auto_ptr作為函數(shù)參數(shù)按值傳遞,因?yàn)樵诤瘮?shù)調(diào)用過程中在函數(shù)的作用域中會(huì)產(chǎn)生一個(gè)局部對(duì)象來接收傳入的auto_ptr(拷貝構(gòu)造),這樣,傳入的實(shí)參auto_ptr就失去了其對(duì)原對(duì)象的所有權(quán),而該對(duì)象會(huì)在函數(shù)退出時(shí)被局部auto_ptr刪除。
如下:
因?yàn)檫@種情況太隱蔽,太容易出錯(cuò)了,所以auto_ptr作為函數(shù)參數(shù)按值傳遞是一定要避免的。或許大家會(huì)想到用auto_ptr的指針或引用作為函數(shù)參數(shù)或許可以,但是仔細(xì)想想,我們并不知道在函數(shù)中對(duì)傳入的auto_ptr做了什么,如果當(dāng)中某些操作使其失去了對(duì)對(duì)象的所有權(quán),那么這還是可能會(huì)導(dǎo)致致命的執(zhí)行期錯(cuò)誤。也許用const reference的形式來傳遞auto_ptr會(huì)是一個(gè)不錯(cuò)的選擇。
(2) 我們可以看到拷貝構(gòu)造函數(shù)與賦值函數(shù)都提供了一個(gè)成員模板在不覆蓋“正統(tǒng)"版本的情況下實(shí)現(xiàn)auto_ptr的隱式轉(zhuǎn)換。
如我們有以下兩個(gè)類:
那么下列代碼就可以通過,實(shí)現(xiàn)從auto_ptr到auto_ptr的隱式轉(zhuǎn)換,因?yàn)锽ase可以轉(zhuǎn)換成Object類型。
auto_ptr<object> apobj = auto_ptr<Base>(new Base);(3) 因?yàn)閍uto_ptr不具有值語義(value semantics),所以auto_ptr不能被用在stl標(biāo)準(zhǔn)容器中。
什么是值語義?簡(jiǎn)單的說,所有的內(nèi)置類型〈primitive variables)都具有value semantics。有人也稱它為POD(plain old data),也就是舊時(shí)的老數(shù)據(jù)(有和OOP的新型抽象數(shù)據(jù)對(duì)比之意)。
對(duì)一個(gè) 具有值語義 的變量賦值可以轉(zhuǎn)換成內(nèi)存的bit-wise-copy(按位拷貝)。
什么樣的類沒有值語義呢?我們不妨稱這種型為none-value-semantics type (NVST).
- 有virtual function的類
- 包含NVST成員的類
- NVST的衍生類(derived classed)
- 定義了自己的= operator的類
- 繼承virtual基類的衍生類
很明顯,auto_ptr不符合上述條件,而我們知道stl標(biāo)準(zhǔn)容器要用到大量的拷貝賦值操作,并且假設(shè)其操作的類型必須符合以上條件。
4. 提領(lǐng)操作(dereference)
提領(lǐng)擁有兩個(gè)操作
一是返回其所擁有的對(duì)象的引用
二是實(shí)現(xiàn)了通過auto_ptr調(diào)用其所擁有的對(duì)象的成員。
如:
我們首先要確保這個(gè)智能指針確實(shí)擁有某個(gè)對(duì)象,否則,這個(gè)操作行為即對(duì)空指針的提領(lǐng)是未定義。
5. 輔助函數(shù)
(1) get用來顯式的返回auto_ptr所擁有的對(duì)象指針。我們可以發(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)擁有某對(duì)象,必須先釋放該對(duì)象。
6. 使用auto_ptr的注意事項(xiàng)
(1) auto_ptr 不能指向數(shù)組
(2) auto_ptr 不能共享所有權(quán)
(3) auto_ptr 不能通過復(fù)制操作來初始化
(4) auto_ptr 不能放入容器中使用
(5) auto_ptr 不能作為容器的成員
(6) 不能把一個(gè)原生指針給兩個(gè)智能指針對(duì)象管理(對(duì)所有的智能指針)
總結(jié)
以上是生活随笔為你收集整理的智能指针——auto_ptr的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux】虚拟地址空间
- 下一篇: 智能指针——unique_ptr