智能指针学习
最近接觸到智能指針很多,于是研究了一下智能指針的原理,寫(xiě)下自己的心得體會(huì),有不對(duì)的還請(qǐng)指正。
智能指針產(chǎn)生的目的:因?yàn)樵?span lang="EN-US">C++中,存在非常復(fù)雜的指針錯(cuò)誤問(wèn)題,例如,某個(gè)對(duì)象生成后,指向該對(duì)象的指針可能有多個(gè),當(dāng)我們用delete語(yǔ)句刪除其中的一個(gè)指針后,對(duì)象就被銷(xiāo)毀,那么其余指向該對(duì)象的指針就會(huì)懸空,這樣很容易出錯(cuò)內(nèi)存誤,為避免出現(xiàn)這樣的問(wèn)題,出現(xiàn)了智能指針,智能指針有2種構(gòu)造方法,一種是插入式的還有一種是非插入式的,非插入式指針一般是直接采用裸指針作為參數(shù)進(jìn)行創(chuàng)建,不需要修改現(xiàn)有的對(duì)象代碼,而插入式是采用一個(gè)公用的有數(shù)量統(tǒng)計(jì)功能的基類(lèi)來(lái)派生需要智能指針的類(lèi),相對(duì)來(lái)說(shuō),插入式構(gòu)造方法將需要更多的額外空間,而且需要修改原類(lèi)。非插入式智能指針(shared_ptr)可以從裸指針,另一個(gè)shared_ptr、一個(gè)std::auto_ptr、或者一個(gè)boost::weak_ptr來(lái)構(gòu)造,還可以傳遞第二個(gè)參數(shù)給shared_ptr的構(gòu)造函數(shù),它被稱(chēng)為刪除器(deleter)。刪除器稍后會(huì)被調(diào)用,來(lái)處理共享資源的釋放。這對(duì)于管理那些不是用new分配也不是用delete釋放的資源時(shí)非常有用。shared_ptr被創(chuàng)建后,它就可象普通指針一樣使用了,除了一點(diǎn),它不能被顯式地刪除,列舉一個(gè)shared_ptr的例子,有時(shí)候把對(duì)象直接存入容器中有時(shí)會(huì)有些麻煩,以值的方式保存對(duì)象意味著使用者將獲得容器中的元素的拷貝,對(duì)于那些復(fù)制是一種昂貴的操作的類(lèi)型來(lái)說(shuō)可能會(huì)有性能的問(wèn)題。此外,有些容器,特別是 std::vector, 當(dāng)你加入元素時(shí)可能會(huì)復(fù)制所有元素,這更加重了性能的問(wèn)題。最后,傳值的語(yǔ)義意味著沒(méi)有多態(tài)的行為。如果你需要在容器中存放多態(tài)的對(duì)象而且你不想切割它們,你必須用指針。如果你用裸指針,維護(hù)元素的完整性會(huì)非常復(fù)雜。從容器中刪除元素時(shí),你必須知道容器的使用者是否還在引用那些要?jiǎng)h除的元素,不用擔(dān)心多個(gè)使用者使用同一個(gè)元素。這些問(wèn)題都可以用shared_ptr來(lái)解決。插入式版本。有時(shí)我們必須使用插入式的引用計(jì)數(shù)智能指針。典型的情況是對(duì)于那些已經(jīng)寫(xiě)好了內(nèi)部引用計(jì)數(shù)器的代碼,而我們又沒(méi)有時(shí)間去重寫(xiě)它(或者已經(jīng)不能獲得那些代碼了)。另一種情況是要求智能指針的大小必須與裸指針大小嚴(yán)格相等,或者shared_ptr的引用計(jì)數(shù)器分配嚴(yán)重影響了程序的性能(這是非常罕見(jiàn)的情況!)。從功能的觀點(diǎn)來(lái)看,唯一需要插入式智能指針的情況是,被指類(lèi)的某個(gè)成員函數(shù)需要返回this,以便它可以用于另一個(gè)智能指針(事實(shí)上,也有辦法使用非插入式智能指針來(lái)解決這個(gè)問(wèn)題)。intrusive_ptr 不同于其它智能指針,因?yàn)樗竽銇?lái)提供它所要的引用計(jì)數(shù)器。當(dāng) intrusive_ptr 遞增或遞減一個(gè)非空指針上的引用計(jì)數(shù)時(shí),它是通過(guò)分別調(diào)用函數(shù) intrusive_ptr_add_ref 和 intrusive_ptr_release來(lái)完成的。這兩個(gè)函數(shù)負(fù)責(zé)確保引用計(jì)數(shù)的正確性,并且負(fù)責(zé)在引用計(jì)數(shù)降為零時(shí)刪除指針。因此,你必須為你的類(lèi)重載這兩個(gè)函數(shù)。
?
?
下面是部分實(shí)現(xiàn)源代碼:
非插入式版本:
namespace boost {template<typename T> class shared_ptr {
public:
template <class Y> explicit shared_ptr(Y* p); //從裸露指針構(gòu)造
template <class Y,class D> shared_ptr(Y* p,D d); //從裸露指針構(gòu)造,同時(shí)指定刪除器
~shared_ptr();
shared_ptr(const shared_ptr & r); //從另一個(gè)指針指針構(gòu)造template <class Y> explicit shared_ptr(const weak_ptr<Y>& r);//從弱指針構(gòu)造
template <class Y> explicit shared_ptr(std::auto_ptr<Y>& r);//從自動(dòng)指針構(gòu)造
shared_ptr& operator=(const shared_ptr& r); void reset();//用于停止對(duì)保存指針的所有權(quán)的共享。共享資源的引用計(jì)數(shù)減一
T& operator*() const;
T* operator->() const;
T* get() const;
bool unique() const;
long use_count() const;
operator unspecified_bool_type() const;?
void swap(shared_ptr<T>& b);
};
template <class T,class U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r);
} ?
插入式版本(只列出了最重要的函數(shù))
namespace boost {
? template<class T> class intrusive_ptr {
? public:
??? intrusive_ptr(T* p,bool add_ref=true);
??? intrusive_ptr(const intrusive_ptr& r);
??? ~intrusive_ptr();
??? T& operator*() const;
??? T* operator->() const;
??? T* get() const; 
??? operator unspecified-bool-type() const; 
? };
? template <class T> T* get_pointer(const intrusive_ptr<T>& p); 
? template <class T,class U> intrusive_ptr<T>
? static_pointer_cast(const intrusive_ptr<U>& r); 
使用intrusive_ptr與使用shared_ptr相比,有兩個(gè)主要的不同之處。第一個(gè)是你需要提供引用計(jì)數(shù)的機(jī)制。第二個(gè)是把this當(dāng)成智能指針是合法的[12],正如我們即將看到的,有時(shí)候這樣很方便。注意,在多數(shù)情況下,應(yīng)該使用非插入式的 shared_ptr. 你不能用shared_ptr 來(lái)做到這一點(diǎn),如果沒(méi)有進(jìn)行特殊處理的話(huà),如 enable_shared_from_this.要使用 boost::intrusive_ptr, 要包含 "boost/intrusive_ptr.hpp" 并定義兩個(gè)普通函數(shù) intrusive_ptr_add_ref 和 intrusive_ptr_release. 它們都要接受一個(gè)參數(shù),即指向你要使用intrusive_ptr的類(lèi)型的指針。這兩個(gè)函數(shù)的返回值被忽略。通常的做法是,泛化這兩個(gè)函數(shù),簡(jiǎn)單地調(diào)用被管理類(lèi)型的成員函數(shù)去完成工作(例如,調(diào)用 add_ref 和 release)。如果引用計(jì)數(shù)降為零,intrusive_ptr_release 應(yīng)該負(fù)責(zé)釋放資源。以下是你應(yīng)該如何實(shí)現(xiàn)這兩個(gè)泛型函數(shù)的示范:
template <typename T> void intrusive_ptr_add_ref(T* t) {
? t->add_ref();
}
template <typename T> void intrusive_ptr_release(T* t) {
? if (t->release()<=0)
??? delete t;
}
注意,這兩個(gè)函數(shù)應(yīng)該定義在它們的參數(shù)類(lèi)型所在的作用域內(nèi)。這意味著如果這個(gè)函數(shù)接受的參數(shù)類(lèi)型來(lái)自于一個(gè)名字空間,則函數(shù)也必須定義在那里。這樣做的原因是,函數(shù)的調(diào)用是非受限的,即允許采用參數(shù)相關(guān)查找,而如果有多個(gè)版本的函數(shù)被提供,那么全部名字空間肯定不是放置它們的好地方。我們稍后將看到一個(gè)關(guān)于如何放置它們的例子,但首先,我們需要提供某類(lèi)的引用計(jì)數(shù)器。
?
explicit關(guān)鍵字: c++中的explicit關(guān)鍵字用來(lái)修飾類(lèi)的構(gòu)造函數(shù),表明該構(gòu)造函數(shù)是顯式的,既然有"顯式"那么必然就有"隱式",那么什么是顯示而什么又是隱式的呢?如果c++類(lèi)的構(gòu)造函數(shù)有一個(gè)參數(shù),那么在編譯的時(shí)候就會(huì)有一個(gè)缺省的轉(zhuǎn)換操作:將該構(gòu)造函數(shù)對(duì)應(yīng)數(shù)據(jù)類(lèi)型的數(shù)據(jù)轉(zhuǎn)換為該類(lèi)對(duì)象,如下面所示:class MyClass
{
public:
MyClass( int num );
}
....
MyClass obj = 10; //ok,convert int to MyClass
在上面的代碼中編譯器自動(dòng)將整型轉(zhuǎn)換為MyClass類(lèi)對(duì)象,實(shí)際上等同于下面的操作:
MyClass temp(10);
MyClass obj = temp;
上面的所有的操作即是所謂的"隱式轉(zhuǎn)換"。 未完待續(xù)。。。。。
?
總結(jié)
 
                            
                        