引用計(jì)數(shù)是一種便利的內(nèi)存管理機(jī)制,但它有一個很大的缺點(diǎn),那就是不能管理循環(huán)引用的對象。一個簡單的例子如下:
#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
class parent;
class children;
typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<children> children_ptr;
class parent
{
public:
~parent() { std::cout <<"destroying parent\n"; }
public:
children_ptr children;
};
class children
{
public:
~children() { std::cout <<"destroying children\n"; }
public:
parent_ptr parent;
};
void test()
{
parent_ptr father(new parent());
children_ptr son(new children);
father->children = son;
son->parent = father;
}
void main()
{
std::cout<<"begin test...\n";
test();
std::cout<<"end test.\n";
}
運(yùn)行該程序可以看到,即使退出了test函數(shù)后,由于parent和children對象互相引用,它們的引用計(jì)數(shù)都是1,不能自動釋放,并且此時這兩個對象再無法訪問到。這就引起了c++中那臭名昭著的內(nèi)存泄漏。
一般來講,解除這種循環(huán)引用有下面有三種可行的方法:
雖然這三種方法都可行,但方法1和方法2都需要程序員手動控制,麻煩且容易出錯。這里主要介紹一下第三種方法和boost中的弱引用的智能指針boost::weak_ptr。
強(qiáng)引用和弱引用
一個強(qiáng)引用當(dāng)被引用的對象活著的話,這個引用也存在(就是說,當(dāng)至少有一個強(qiáng)引用,那么這個對象就不能被釋放)。boost::share_ptr就是強(qiáng)引用。
相對而言,弱引用當(dāng)引用的對象活著的時候不一定存在。僅僅是當(dāng)它存在的時候的一個引用。弱引用并不修改該對象的引用計(jì)數(shù),這意味這弱引用它并不對對象的內(nèi)存進(jìn)行管理,在功能上類似于普通指針,然而一個比較大的區(qū)別是,弱引用能檢測到所管理的對象是否已經(jīng)被釋放,從而避免訪問非法內(nèi)存。
boost::weak_ptr
boost::weak_ptr<T>是boost提供的一個弱引用的智能指針,它的聲明可以簡化如下:
namespace boost {
template<typename T> class weak_ptr {
public:
template <typename Y>
weak_ptr(const shared_ptr<Y>& r);
weak_ptr(const weak_ptr& r);
~weak_ptr();
T* get() const;
bool expired() const;
shared_ptr<T> lock() const;
};
}
可以看到,boost::weak_ptr必須從一個boost::share_ptr或另一個boost::weak_ptr轉(zhuǎn)換而來,這也說明,進(jìn)行該對象的內(nèi)存管理的是那個強(qiáng)引用的boost::share_ptr。boost::weak_ptr只是提供了對管理對象的一個訪問手段。
boost::weak_ptr除了對所管理對象的基本訪問功能(通過get()函數(shù))外,還有兩個常用的功能函數(shù):expired()用于檢測所管理的對象是否已經(jīng)釋放;lock()用于獲取所管理的對象的強(qiáng)引用指針。
通過boost::weak_ptr來打破循環(huán)引用
由于弱引用不更改引用計(jì)數(shù),類似普通指針,只要把循環(huán)引用的一方使用弱引用,即可解除循環(huán)引用。對于上面的那個例子來說,只要把children的定義改為如下方式,即可解除循環(huán)引用:
class children
{
public:
~children() { std::cout <<"destroying children\n"; }
public:
boost::weak_ptr<parent> parent;
};
最后值得一提的是,雖然通過弱引用指針可以有效的解除循環(huán)引用,但這種方式必須在程序員能預(yù)見會出現(xiàn)循環(huán)引用的情況下才能使用,也可以是說這個僅僅是一種編譯期的解決方案,如果程序在運(yùn)行過程中出現(xiàn)了循環(huán)引用,還是會造成內(nèi)存泄漏的。因此,不要認(rèn)為只要使用了智能指針便能杜絕內(nèi)存泄漏。畢竟,對于C++來說,由于沒有垃圾回收機(jī)制,內(nèi)存泄漏對每一個程序員來說都是一個非常頭痛的問題。
two weak_ptr instancesare equivalent if and only if they share ownership or are both empty.
2、scoped_ptr、scoped_array
scoped_ptr與auto_ptr相似, scoped_ptr智能指針與std::auto_ptr不同在于,它明確禁止任何想要這樣做的企圖!這在你需要確保指針任何時候只有一個擁有者時的任何一種情境下都是非常重要的
scoped_ptr通過成員還是的private私有化來禁止拷貝 nocopyable
scoped_array與scoped_ptr顯然是意義等價的,但是是用來處理數(shù)組的
3、shared_ptr,shared_array
boost::scoped_ptr雖然簡單易用,但它不能共享所有權(quán)的特性卻大大限制了其使用范圍,而 boost::shared_ptr可以解決這一局限。顧名思義,boost::shared_ptr是可以共享所有權(quán)的智能指針,首先讓我們通過一個例子看看它的基本用法:
#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
class implementation
{
public:
~implementation() { std::cout <<"destroying implementation\n"; }
void do_something() { std::cout << "did something\n"; }
};
void test()
{
boost::shared_ptr<implementation> sp1(new implementation());
std::cout<<"The Sample now has "<<sp1.use_count()<<" references\n";
boost::shared_ptr<implementation> sp2 = sp1;
std::cout<<"The Sample now has "<<sp2.use_count()<<" references\n";
sp1.reset();
std::cout<<"After Reset sp1. The Sample now has "<<sp2.use_count()<<" references\n";
sp2.reset();
std::cout<<"After Reset sp2.\n";
}
void main()
{
test();
}
該程序的輸出結(jié)果如下:
The Sample now has 1 references
The Sample now has 2 references
After Reset sp1. The Sample now has 1 references
destroying implementation
After Reset sp2.
可以看到,boost::shared_ptr指針sp1和sp2同時擁有了implementation對象的訪問權(quán)限,且當(dāng)sp1和sp2都釋放對該對象的所有權(quán)時,其所管理的的對象的內(nèi)存才被自動釋放。在共享對象的訪問權(quán)限同時,也實(shí)現(xiàn)了其內(nèi)存的自動管理。
boost::shared_ptr的內(nèi)存管理機(jī)制:
boost::shared_ptr的管理機(jī)制其實(shí)并不復(fù)雜,就是對所管理的對象進(jìn)行了引用計(jì)數(shù),當(dāng)新增一個boost::shared_ptr對該對象進(jìn)行管理時,就將該對象的引用計(jì)數(shù)加一;減少一個boost::shared_ptr對該對象進(jìn)行管理時,就將該對象的引用計(jì)數(shù)減一,如果該對象的引用計(jì)數(shù)為0的時候,說明沒有任何指針對其管理,才調(diào)用delete釋放其所占的內(nèi)存。
上面的那個例子可以的圖示如下:
boost::shared_ptr的特點(diǎn):
和前面介紹的boost::scoped_ptr相比,boost::shared_ptr可以共享對象的所有權(quán),因此其使用范圍基本上沒有什么限制(還是有一些需要遵循的使用規(guī)則,下文中介紹),自然也可以使用在stl的容器中。另外它還是線程安全的,這點(diǎn)在多線程程序中也非常重要。
boost::shared_ptr的使用規(guī)則:
boost::shared_ptr并不是絕對安全,下面幾條規(guī)則能使我們更加安全的使用boost::shared_ptr:
一 Boost::smart_Ptr
我們學(xué)習(xí)C++都知道智能指針,例如STL中的std::auto_ptr,但是為什么要使用智能指針,使用它能帶給我們什么好處呢?
最簡單的使用智能指針可以不會因?yàn)橥沝elete指針而造成內(nèi)存泄露。還有如果我們開發(fā)或者使用第三方的lib中的某些函數(shù)需要返回指針,這樣的返回的指針被client使用的時候,lib就會失去對返回的指針的控制,這樣delete的指針的任務(wù)一般就會交給調(diào)用方client,但是如果client忘記調(diào)用delete或是調(diào)用的時機(jī)不正確,都有可能導(dǎo)致問題,在這種情況下就最好使用智能指針。還有使用智能指針可以保證異常安全,保證程序在有異常拋出時仍然無內(nèi)存泄露。
std::auto_ptr很多的時候并不能滿足我們的要求,比如她不能用在STL的container中。boost的smart_ptr中提供了4種智能指針和2種智能指針數(shù)組來作為std::auto_ptr的補(bǔ)充。
shared_ptr<boost/shared_ptr.hpp>:使用shared_ptr進(jìn)行對象的生存期自動管理,使得分享資源所有權(quán)變得有效且安全.
scoped_ptr<boost/scoped_ptr.hpp>:用于確保能夠正確地刪除動態(tài)分配的對象。scoped_ptr有著與std::auto_ptr類似的特性,而最大的區(qū)別在于它不能轉(zhuǎn)讓所有權(quán)而auto_ptr可以。事實(shí)上,scoped_ptr永遠(yuǎn)不能被復(fù)制或被賦值!scoped_ptr 擁有它所指向的資源的所有權(quán),并永遠(yuǎn)不會放棄這個所有權(quán)。
weak_ptr<boost/weak_ptr.hpp>:weak_ptr是 shared_ptr 的觀察員。它不會干擾shared_ptr所共享的所有權(quán)。當(dāng)一個被weak_ptr所觀察的 shared_ptr要釋放它的資源時,它會把相關(guān)的 weak_ptr的指針設(shè)為空。使用此輔助指針一般是防止懸空指針。
intrusive_ptr<boost/intrusive_ptr.hpp>:shared_ptr的插入是版本,一般不使用,因?yàn)樾枰獙κ褂么酥羔樀念愋椭性黾觬ef計(jì)數(shù)功能。但是可以保證不增加指針的大小。
scoped_array<boost/scoped_array.hpp>: scoped_array 為數(shù)組做了scoped_ptr為單個對象的指針?biāo)龅氖虑椋核?fù)責(zé)釋放內(nèi)存。
shared_array<boost/shared_array.hpp>:shared_array用于共享數(shù)組所有權(quán)的智能指針。一般指向std::vector的shared_ptr提供了比shared_array更多的靈活性,所以一般使用std::vector<shared_ptr>。
二 源碼剖析
通過上面的分析,下面主要介紹我們最常用也最有用的shared_ptr智能指針。(智能指針的實(shí)現(xiàn),其實(shí)最重要的是就是重載->和*運(yùn)算符)
下面是shared_ptr的頭文件:
template<class Ty> class shared_ptr {
public:
typedef Ty element_type;
shared_ptr();
template<class Other>
explicit shared_ptr(Other *ptr);
template<class Other, class D>
shared_ptr(Other *ptr, D dtor);
shared_ptr(const shared_ptr& sp);
template<class Other>
shared_ptr(const shared_ptr<Other>& sp);
template <class Other>
shared_ptr(const weak_ptr<Other>& wp);
template<class Other>
shared_ptr(const std::auto_ptr<Other>& ap);
~shared_ptr();
shared_ptr& operator=(const shared_ptr& sp);
template<class Other>
shared_ptr& operator=(const shared_ptr<Other>& sp);
template<class Other>
shared_ptr& operator=(auto_ptr<Other>& ap);
void swap(shared_ptr& s);
void reset();
template<class Other>
void reset(Other *ptr);
template<class Other, class D>
void reset(Other *ptr, D dtor);
Ty *get() const;
Ty& operator*() const;
Ty *operator->() const;
long use_count() const;
bool unique() const;
operator boolean-type() const;
};
1)構(gòu)造函數(shù),可以通過一般指針,std::auto_ptr,boost::shared_ptr,boost::weak_ptr來構(gòu)造,還可以構(gòu)造的時候指定如果delete指針。
2)拷貝構(gòu)造函數(shù)
3)get(), 得到boost::shared_ptr所封裝的指針。
4)*,得到boost::shared_ptr所封裝指針的值。
5)->,用于直接調(diào)用指針的成員。
6)reset(), 用于重設(shè)boost::shared_ptr,或設(shè)為空。
7) swap(), 用于交換2個boost::shared_ptr.
8) use_count(), use_count 函數(shù)返回指針的引用計(jì)數(shù)。
9) unique(),這個函數(shù)在shared_ptr是它所保存指針的唯一擁有者時返回 true ;否則返回 false。 unique 不會拋出異常。
三 實(shí)例
1)構(gòu)造,拷貝構(gòu)造,賦值,get,*,->, reset, swap:
#include "boost/shared_ptr.hpp"
#include <cassert>
#include <iostream>
class A
{
boost::shared_ptr<int> no_;
public:
A(boost::shared_ptr<int> no) : no_(no) {}
void value(int i) { *no_=i; }
};
class B
{
boost::shared_ptr<int> no_;
public:
B(boost::shared_ptr<int> no) : no_(no) {}
int value() const { return *no_; }
};
struct deleter
{
void operator()(int *i)
{
std::cout << "destroying resource at"
<< (void*)i << '\n';
delete i;
}
};
struct S
{
int member;
};
int main()
{
// test for constructor
boost::shared_ptr<int> sp;
boost::shared_ptr<int> sp2((int*)0);
{
boost::shared_ptr<int> sp3(new int(3), deleter());
}
std::auto_ptr<int> temp(new int(10));
boost::shared_ptr<int> sp4(temp);
boost::shared_ptr<int> temp2(new int(14));
boost::shared_ptr<int> sp5(temp2);
// test for using the shared_ptr(get,->,*)
int *ip = new int(3);
std::cout << (void*)ip << '\n';
boost::shared_ptr<int> sp6(ip);
std::cout << (void*)sp6.get () << '\n';
int *ip2 = new int(3);
std::cout << (void*)ip2 << '\n';
boost::shared_ptr<int> sp7(ip2);
std::cout << *sp7 << '\n';
std::cout << (void*)&*sp7 << '\n';
S *s = new S;
s->member = 4;
boost::shared_ptr<S> sp8(s);
std::cout << sp8 -> member << '\n';
// test for assign
std::cout << std::boolalpha;
boost::shared_ptr<int> sp9(new int(0));
boost::shared_ptr<int> sp10 = sp9;
std::cout << "sp0 == sp1:" << (sp9 == sp10) << '\n';
std::cout << "sp0 != sp2:" << (sp9 != sp10) << '\n';
// test funcion: reset and swap
boost::shared_ptr<int> sp11 (new int(0));
boost::shared_ptr<int> sp12 (new int(1));
sp11. swap (sp12);
std::cout<<*sp11<<*sp12<<std::endl;
boost::swap(sp11, sp12);
std::cout<<*sp11<<*sp12<<std::endl;
boost::shared_ptr<int> sp0;
sp0.reset();
boost::shared_ptr<int> sp01(new int(1));
std::cout << *sp01 <<std::endl;
sp01.reset();
//std::cout << *sp01 <<std::endl; //error
sp01.reset(new int(100));
std::cout << *sp01 <<std::endl;
// test for query shared_ptr's state (use_count,unique)
typedef boost::shared_ptr<int> spi;
spi sp13 ;
std::cout << "empty object: " << sp13.use_count() << '\n';
spi sp14 ((int *)0);
std::cout << "null pointer: " << sp14.use_count() << '\n';
spi sp15 (new int);
std::cout << "one object: " << sp15.use_count() << '\n';
{
spi sp16(sp15);
std::cout << "two objects: " << sp15.use_count() << '\n';
std::cout << "two objects: " << sp16.use_count() << '\n';
}
std::cout << "one object: " << sp15.use_count() << '\n';
std::cout << std::boolalpha;
spi sp17;
std::cout << "empty object: " << sp17.unique() << '\n';
spi sp18((int *)0);
std::cout << "null pointer: " << sp18.unique() << '\n';
spi sp19(new int);
std::cout << "one object: " << sp19.unique() << '\n';
{
spi sp20(sp19);
std::cout << "two objects: " << sp19.unique() << '\n';
std::cout << "two objects: " << sp20.unique() << '\n';
}
std::cout << "one object: " << sp19.unique() << '\n';
}
2) 在STL容器中的使用:
#include "boost/shared_ptr.hpp"
#include <vector>
#include <iostream>
class A
{
public:
virtual void sing()
{
std::cout <<"A::sing" <<std::endl;
}
protected:
virtual ~A()
{std::cout<<"~A"<<std::endl;};
};
class B : public A
{
public:
virtual void sing()
{
std::cout << "B:sing"<<std::endl;
}
virtual ~B()
{std::cout<<"~B"<<std::endl;}
};
boost::shared_ptr<A> createA()
{
boost::shared_ptr<A> p(new B());
return p;
}
int main()
{
typedef std::vector<boost::shared_ptr<A> > container_type;
typedef container_type::iterator iterator;
container_type container;
for (int i=0;i<10;++i)
{
container.push_back(createA());
}
std::cout << "The choir is gathered: \n";
iterator end=container.end();
for (iterator it=container.begin();it!=end;++it)
{ (*it)->sing(); }
}
使用 shared_ptr
可以消除對顯式 delete
的使用,但是它沒有提供避免顯式 new
的支持。有用戶反復(fù)地要求提供一個工廠函數(shù),用于創(chuàng)建給定類型的對象并返回一個指向它的 shared_ptr
.除了方便使用和保持風(fēng)格以外,這樣的函數(shù)還具有異常安全性且明顯更快,因?yàn)樗梢詫ο蠛拖鄳?yīng)的控制塊兩者同時使用單次的內(nèi)存分配,以消除shared_ptr
的構(gòu)造過程中最大的一部分開銷。這消除了對于 shared_ptr
的一個主要的抱怨。
頭文件 <boost/make_shared.hpp> 提供了一組重載的函數(shù)模板,make_shared
和 allocate_shared
, 它們解決了這些需要。make_shared
使用全局的 operator new
來分配內(nèi)存,而 allocate_shared
則使用用戶所提供的分配器,可以更好地進(jìn)行控制。
選擇 make_shared
這個名字的原因是,表達(dá)式make_shared<Widget>()
可以大聲地讀出來,并且傳達(dá)了準(zhǔn)確的意義。
namespace boost {
template<typename T> class shared_ptr;
template<typename T>
shared_ptr<T> make_shared();
template<typename T, typename A>
shared_ptr<T> allocate_shared( A const & );
#if defined( BOOST_HAS_VARIADIC_TMPL ) && defined( BOOST_HAS_RVALUE_REFS ) // C++0x prototypes
template<typename T, typename... Args>
shared_ptr<T> make_shared( Args && ... args );
template<typename T, typename A, typename... Args>
shared_ptr<T> allocate_shared( A const & a, Args && ... args );
#else // no C++0X support
template<typename T, typename Arg1 >
shared_ptr<T> make_shared( Arg1 const & arg1 );
template<typename T, typename Arg1, typename Arg2 >
shared_ptr<T> make_shared( Arg1 const & arg1, Arg2 const & arg2 );
// ...
template<typename T, typename Arg1, typename Arg2, ..., typename ArgN >
shared_ptr<T> make_shared( Arg1 const & arg1, Arg2 const & arg2, ..., ArgN const & argN );
template<typename T, typename A, typename Arg1 >
shared_ptr<T> allocate_shared( A const & a, Arg1 const & arg1 );
template<typename T, typename A, typename Arg1, typename Arg2 >
shared_ptr<T> allocate_shared( Arg1 const & arg1, Arg2 const & arg2 );
// ...
template<typename T, typename A, typename Arg1, typename Arg2, ..., typename ArgN >
shared_ptr<T> allocate_shared( A const & a, Arg1 const & arg1, Arg2 const & arg2, ..., ArgN const & argN );
#endif
}
template<class T, class... Args>
shared_ptr<T> make_shared( Args && ... args );
template<class T, class A, class... Args>
shared_ptr<T> allocate_shared( A const & a, Args && ... args );
要求:表達(dá)式
new( pv ) T( std::forward<Args>(args)... )
是良好定義的,其中pv
是一個void*
,指向適合保存一個類型為T
的對象的一塊內(nèi)存。A
應(yīng)該是一個分配器,符合在C++標(biāo)準(zhǔn)的第20.1.5節(jié)(分配器要求)中的描述。 A
的復(fù)制構(gòu)造函數(shù)和析構(gòu)函數(shù)不能拋出。效果:分配一塊可以保存一個類型為
T
的對象的內(nèi)存,并通過 placement new 表達(dá)式new( pv ) T()
或new( pv ) T( std::forward<Args>(args)... )
在其中構(gòu)造一個對象。allocate_shared
使用a
的一個拷貝來分配內(nèi)存。如果有異常拋出,則沒有影響。返回:一個
shared_ptr
實(shí)例,它保存并擁有這個新構(gòu)造的類型為T
的對象的地址。后驗(yàn)條件:
get() != 0 && use_count() == 1
.拋出:
bad_alloc
, 或是由A::allocate
或T
的構(gòu)造函數(shù)拋出的異常。說明:該實(shí)現(xiàn)在單次內(nèi)存分配中分配了所返回的
shared_ptr
以及一個類型為T
的對象所要的內(nèi)存。這提供了和介入式智能指針同樣的效率。如果你的編譯支持右值引用和可變參數(shù)數(shù)量模板,則使用以上原型。它們完美地將
args
參數(shù)前轉(zhuǎn)給T
的構(gòu)造函數(shù)。否則,本實(shí)現(xiàn)將退回至將參數(shù)以常量引用的方式前轉(zhuǎn)給
T
的構(gòu)造函數(shù)。如果你需要傳遞一個非常量引用給T
的構(gòu)造函數(shù),你可以通過將參數(shù)包裝在一個boost::ref
調(diào)用中來實(shí)現(xiàn)。另外,你被限制為最多使用9個參數(shù)(不計(jì)allocate_shared 的分配器參數(shù))。
boost::shared_ptr<std::string> x = boost::make_shared<std::string>("hello, world!");
std::cout << *x;
intrusive_ptr 類模板存儲一個指向帶有侵入式引用計(jì)數(shù)的對象的指針。每一個新的 intrusive_ptr 實(shí)例都通過對函數(shù) intrusive_ptr_add_ref 的無條件調(diào)用(將指針作為參數(shù))增加引用計(jì)數(shù)。同樣,當(dāng)一個 intrusive_ptr 被銷毀,它會調(diào)用 intrusive_ptr_release,這個函數(shù)負(fù)責(zé)當(dāng)引用計(jì)數(shù)降為 0 時銷毀這個對象。這兩個函數(shù)的適當(dāng)定義由用戶提供。在支持 argument-dependent lookup (參數(shù)依賴查找)的編譯器上,intrusive_ptr_add_ref 和 intrusive_ptr_release 應(yīng)該和它們的參數(shù)定義在同一個名字空間中,否則,就定義名字空間 boost 中。
這個類模板以 T 為參數(shù),T 是被指向的對象的類型。只要 T* 能被隱式地轉(zhuǎn)換到 U*,則 intrusive_ptr<T> 就能被隱式地轉(zhuǎn)換到 intrusive_ptr<U>。
使用 intrusive_ptr 的主要原因是:
作為一個通用規(guī)則,如果 intrusive_ptr 不是很明顯地比 shared_ptr 更加適合你的需要,請首先考慮基于 shared_ptr 的設(shè)計(jì)。
namespace boost {
template<class T> class intrusive_ptr {
public:
typedef T element_type;
intrusive_ptr(); // never throws
intrusive_ptr(T * p, bool add_ref = true);
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
~intrusive_ptr();
intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
template<class Y> intrusive_ptr & operator=(T * r);
void reset(T * r);
T & operator*() const; // never throws
T * operator->() const; // never throws
T * get() const; // never throws
operator unspecified-bool-type() const; // never throws
void swap(intrusive_ptr & b); // never throws
};
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws
template<class T>
bool operator==(intrusive_ptr<T> const & a, T * b); // never throws
template<class T>
bool operator!=(intrusive_ptr<T> const & a, T * b); // never throws
template<class T>
bool operator==(T * a, intrusive_ptr<T> const & b); // never throws
template<class T>
bool operator!=(T * a, intrusive_ptr<T> const & b); // never throws
template<class T, class U>
bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws
template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b); // never throws
template<class T> T * get_pointer(intrusive_ptr<T> const & p); // never throws
template<class T, class U>
intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r); // never throws
template<class T, class U>
intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r); // never throws
template<class T, class U>
intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r); // never throws
template<class E, class T, class Y>
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, intrusive_ptr<Y> const & p);
}
typedef T element_type;
提供模板參數(shù) T 的類型。
intrusive_ptr(); // never throws
后置條件:
get() == 0
。拋出:無。
intrusive_ptr(T * p, bool add_ref = true);
作用:
if(p != 0 && add_ref) intrusive_ptr_add_ref(p);
。后置條件:
get() == p
。
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
作用:
if(r.get() != 0) intrusive_ptr_add_ref(r.get());
。后置條件:
get() == r.get()
。
~intrusive_ptr();
作用:
if(get() != 0) intrusive_ptr_release(get());
。
intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);
作用:等價于
intrusive_ptr(r).swap(*this)
。返回:
*this
。
void reset(T * r);
作用:等價于
intrusive_ptr(r).swap(*this)
.
T & operator*() const; // never throws
條件:
get() != 0
。返回:
*get()
。拋出:無。
T * operator->() const; // never throws
條件:
get() != 0
。返回:
get()
。拋出:無。
T * get() const; // never throws
返回:所存儲的指針。
拋出:無。
operator unspecified-bool-type () const; // never throws
返回:一個未確定的值,在需要布爾值的上下文中,它等價于
get() != 0
。拋出:無。
注意:這一轉(zhuǎn)換操作符允許將 intrusive_ptr 對象用于需要布爾值的上下文中,就像
if (p && p->valid()) {}
。實(shí)際目標(biāo)類型通常是一個指向成員函數(shù)的指針,消除了很多隱式轉(zhuǎn)換的陷阱。
void swap(intrusive_ptr & b); // never throws
作用:交換兩個智能指針中的內(nèi)容。
拋出:無。
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws
返回:
a.get() == b.get()
。拋出:無。
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws
返回:
a.get() != b.get()
。拋出:無。
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, U * b); // never throws
返回:
a.get() == b
。拋出:無。
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, U * b); // never throws
返回:
a.get() != b
。拋出:無。
template<class T, class U>
bool operator==(T * a, intrusive_ptr<U> const & b); // never throws
返回:
a == b.get()
。拋出:無。
template<class T, class U>
bool operator!=(T * a, intrusive_ptr<U> const & b); // never throws
返回:
a != b.get()
。拋出:無。
template<class T, class U>
bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws
返回:
std::less<T *>()(a.get(), b.get())
。拋出:無。
注意:允許 intrusive_ptr 對象在關(guān)聯(lián)式容器中作為鍵值使用。
template<class T>
void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b); // never throws
作用:等價于
a.swap(b)
。拋出:無。
注意:與 std::swap 的接口匹配。為泛型編程提供幫助。
template<class T>
T * get_pointer(intrusive_ptr<T> const & p); // never throws
返回:
p.get()
。拋出:無。
注意:為泛型編程提供幫助,用于 mem_fn。
template<class T, class U>
intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r); // never throws
返回:
intrusive_ptr<T>(static_cast<T*>(r.get()))
。拋出:無。
template<class T, class U>
intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r); // never throws
返回:
intrusive_ptr<T>(const_cast<T*>(r.get()))
。拋出:無。
template<class T, class U>
intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r);
返回:
intrusive_ptr<T>(dynamic_cast<T*>(r.get()))
。拋出:無。
template<class E, class T, class Y>
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, intrusive_ptr<Y> const & p);
作用:
os << p.get();
。返回:
os
。
intrusive_ptr 是shared_ptr的插入式版本。有時我們必須使用插入式的引用計(jì)數(shù)智能指針。典型的情況是對于那些已經(jīng)寫好了內(nèi)部引用計(jì)數(shù)器的代碼,而我們又沒有時間去重寫它(或者已經(jīng)不能獲得那些代碼了)。另一種情況是要求智能指針的大小必須與裸指針大小嚴(yán)格相等,或者shared_ptr的引用計(jì)數(shù)器分配嚴(yán)重影響了程序的性能(我可以肯定這是非常罕見的情況!)。從功能的觀點(diǎn)來看,唯一需要插入式智能指針的情況是,被指類的某個成員函數(shù)需要返回this,以便它可以用于另一個智能指針(事實(shí)上,也有辦法使用非插入式智能指針來解決這個問題,正如我們在本章前面看到的)。intrusive_ptr 不同于其它智能指針,因?yàn)樗竽銇硖峁┧囊糜?jì)數(shù)器。
當(dāng) intrusive_ptr 遞增或遞減一個非空指針上的引用計(jì)數(shù)時,它是通過分別調(diào)用函數(shù) intrusive_ptr_add_ref 和 intrusive_ptr_release來完成的。這兩個函數(shù)負(fù)責(zé)確保引用計(jì)數(shù)的正確性,并且負(fù)責(zé)在引用計(jì)數(shù)降為零時刪除指針。因此,你必須為你的類重載這兩個函數(shù),正如我們后面將看到的。
以下是intrusive_ptr的部分摘要,只列出了最重要的函數(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(T* p,bool add_ref=true);
這個構(gòu)造函數(shù)將指針p保存到*this中。如果 p 非空,并且 add_ref 為 true, 構(gòu)造函數(shù)將調(diào)用 intrusive_ptr_add_ref(p). 如果 add_ref 為 false, 構(gòu)造函數(shù)則不調(diào)用 intrusive_ptr_add_ref. 如果intrusive_ptr_add_ref會拋出異常,則構(gòu)造函數(shù)也會。
intrusive_ptr(const intrusive_ptr& r);
該復(fù)制構(gòu)造函數(shù)保存一份r.get()的拷貝,并且如果指空非空則用它調(diào)用 intrusive_ptr_add_ref 。這個構(gòu)造函數(shù)不會拋出異常。
~intrusive_ptr();
如果保存的指針為非空,則 intrusive_ptr 的析構(gòu)函數(shù)會以保存的指針為參數(shù)調(diào)用函數(shù) intrusive_ptr_release。 intrusive_ptr_release 負(fù)責(zé)遞減引用計(jì)數(shù)并在計(jì)數(shù)為零時刪除指針。這個函數(shù)不會拋出異常。
T& operator*() const;
解引用操作符返回所存指針的解引用。如果所存指針為空則會導(dǎo)致未定義行為。你應(yīng)該確認(rèn)intrusive_ptr有一個非空的指針,這可以用函數(shù) get 實(shí)現(xiàn),或者在Boolean上下文中測試 intrusive_ptr 。解引用操作符不會拋出異常。
T* operator->() const;
這個操作符返回保存的指針。在引用的指針為空時調(diào)用這個操作符會有未定義行為。這個操作符不會拋出異常。
T* get() const;
這個成員函數(shù)返回保存的指針。它可用于你需要一個裸指針的時候,即使保存的指針為空也可以調(diào)用。這個函數(shù)不會拋出異常。
operator unspecified-bool-type() const;
這個類型轉(zhuǎn)換函數(shù)返回一個可用于布爾表達(dá)式的類型,而它絕對不是 operator bool, 因?yàn)槟菢訒试S一些必須要禁止的操作。這個轉(zhuǎn)換允許intrusive_ptr在一個布爾上下文中被測試,例如,if (p), p 是一個 intrusive_ptr. 這個轉(zhuǎn)換函數(shù)當(dāng)intrusive_ptr引向一個非空指針時返回True ; 否則返回 false. 這個轉(zhuǎn)換函數(shù)不會拋出異常。
template <class T> T* get_pointer(const intrusive_ptr<T>& p);
這個函數(shù)返回 p.get(), 它主要用于支持泛型編程。[10] 它也可以用作替代成員函數(shù) get, 因?yàn)樗梢灾剌d為可以與裸指針或第三方智能指針類一起工作。有些人寧愿用普通函數(shù)而不用成員函數(shù)。[11] 這個函數(shù)不會拋出異常。
[10] 這種函數(shù)被稱為 shims. 見參考書目的 [12] 。
[11]這種想法是出于以下原因,使用智能指針的成員函數(shù)時,很難分清它是操作智能指針還是操作它所指向的對象。例如, p.get() 和 p->get() 有完全不同的意思,不認(rèn)真看還很難區(qū)別,而 get_pointer(p) 和 p->get() 則一看就知道不一樣。對于你來說這是不是問題,主要取決于你的感覺和經(jīng)驗(yàn)。
template <class T,class U>
intrusive_ptr<T> static_pointer_cast(const intrusive_ptr<U>& r);
這個函數(shù)返回 intrusive_ptr<T>(static_cast<T*>(r.get())). 和 shared_ptr不一樣,你可以對保存在intrusive_ptr中的對象指針安全地使用static_cast。但是你可能出于對智能指針類型轉(zhuǎn)換的用法一致性而想使用這個函數(shù)。static_pointer_cast 不會拋出異常。
使用intrusive_ptr與使用shared_ptr相比,有兩個主要的不同之處。第一個是你需要提供引用計(jì)數(shù)的機(jī)制。第二個是把this當(dāng)成智能指針是合法的[12],正如我們即將看到的,有時候這樣很方便。注意,在多數(shù)情況下,應(yīng)該使用非插入式的 shared_ptr.
[12] 你不能用shared_ptr 來做到這一點(diǎn),如果沒有進(jìn)行特殊處理的話,如 enable_shared_from_this.
要使用 boost::intrusive_ptr, 要包含 "boost/intrusive_ptr.hpp" 并定義兩個普通函數(shù) intrusive_ptr_add_ref 和 intrusive_ptr_release. 它們都要接受一個參數(shù),即指向你要使用intrusive_ptr的類型的指針。這兩個函數(shù)的返回值被忽略。通常的做法是,泛化這兩個函數(shù),簡單地調(diào)用被管理類型的成員函數(shù)去完成工作(例如,調(diào)用 add_ref 和 release)。如果引用計(jì)數(shù)降為零,intrusive_ptr_release 應(yīng)該負(fù)責(zé)釋放資源。以下是你應(yīng)該如何實(shí)現(xiàn)這兩個泛型函數(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;
}
注意,這兩個函數(shù)應(yīng)該定義在它們的參數(shù)類型所在的作用域內(nèi)。這意味著如果這個函數(shù)接受的參數(shù)類型來自于一個名字空間,則函數(shù)也必須定義在那里。這樣做的原因是,函數(shù)的調(diào)用是非受限的,即允許采用參數(shù)相關(guān)查找,而如果有多個版本的函數(shù)被提供,那么全部名字空間肯定不是放置它們的好地方。我們稍后將看到一個關(guān)于如何放置它們的例子,但首先,我們需要提供某類的引用計(jì)數(shù)器。
現(xiàn)在管理用的函數(shù)已經(jīng)定義了,我們必須要提供一個內(nèi)部的引用計(jì)數(shù)器了。在本例中,引用計(jì)數(shù)是一個初始化為零的私有數(shù)據(jù)成員,我們將公開 add_ref 和 release 成員函數(shù)來操作它。add_ref 遞增引用計(jì)數(shù)而 release 遞減它[13]。 我們可以增加一個返回引用計(jì)數(shù)當(dāng)前值的成員函數(shù),但release也可以做到這一點(diǎn)。下面的基類,reference_counter, 提供了一個計(jì)數(shù)器以及 add_ref 和 release 成員函數(shù),我們可以簡單地用繼承來為一個類增加引用計(jì)數(shù)了。
[13] 注意,在多線程環(huán)境下,對保持引用計(jì)數(shù)的變量的任何操作都必須同步化。
class reference_counter {
int ref_count_;
public:
reference_counter() : ref_count_(0) {}
virtual ~reference_counter() {}
void add_ref() {
++ref_count_;
}
int release() {
return --ref_count_;
}
protected:
reference_counter& operator=(const reference_counter&) {
// 無操作
return *this;
}
private:
// 禁止復(fù)制構(gòu)造函數(shù)
reference_counter(const reference_counter&);
};
把reference_counter的析構(gòu)函數(shù)聲明為虛擬的原因是這個類將被公開繼承,有可能會使用一個reference_counter指針來delete派生類。我們希望刪除操作能夠正確地調(diào)用派生類的析構(gòu)函數(shù)。實(shí)現(xiàn)非常簡單:add_ref 遞增引用計(jì)數(shù),release 遞減引用計(jì)數(shù)并返回它。要使用這個引用計(jì)數(shù),要做的就是公共地繼承它。以下是一個類 some_ class ,包含一個內(nèi)部引用計(jì)數(shù),并使用 intrusive_ptr。
#include <iostream>
#include "boost/intrusive_ptr.hpp"
class some_class : public reference_counter {
public:
some_class() {
std::cout << "some_class::some_class()\n";
}
some_class(const some_class& other) {
std::cout << "some_class(const some_class& other)\n";
}
~some_class() {
std::cout << "some_class::~some_class()\n";
}
};
int main() {
std::cout << "Before start of scope\n";
{
boost::intrusive_ptr<some_class> p1(new some_class());
boost::intrusive_ptr<some_class> p2(p1);
}
std::cout << "After end of scope \n";
}
為了顯示 intrusive_ptr以及函數(shù) intrusive_ptr_add_ref 和 intrusive_ptr_release 都正確無誤,以下是這個程序的運(yùn)行輸出:
Before start of scope
some_class::some_class()
some_class::~some_class()
After end of scope
intrusive_ptr 為我們打點(diǎn)一切。當(dāng)?shù)谝粋€ intrusive_ptr p1 創(chuàng)建時,它傳送了一個some_class的新實(shí)例。intrusive_ptr 構(gòu)造函數(shù)實(shí)際上有兩個參數(shù),第二個是一個 bool ,表示是否要調(diào)用 intrusive_ptr_add_ref 。由于這個參數(shù)的缺省值是 True, 所以在構(gòu)造 p1時,some_class實(shí)例的引用計(jì)數(shù)變?yōu)?。然后,第二個 intrusive_ptr, p2初構(gòu)造。它是從 p1復(fù)制構(gòu)造的,當(dāng) p2 看到 p1 是引向一個非空指針時,它調(diào)用 intrusive_ptr_add_ref. 引用計(jì)數(shù)變?yōu)?。然后,兩個 intrusive_ptr都離開作用域了。首先, p2 被銷毀,析構(gòu)函數(shù)調(diào)用 intrusive_ptr_release. 它把引用計(jì)數(shù)減為1。然后,p1 被銷毀,析構(gòu)函數(shù)再次調(diào)用 intrusive_ptr_release ,導(dǎo)致引用計(jì)數(shù)降為0;這使得我們的intrusive_ptr_release 去 delete 該指針。你可能注意到 reference_counter 的實(shí)現(xiàn)不是線程安全的,因此不能用于多線程應(yīng)用,除非加上同步化。
比起依賴于intrusive_ptr_add_ref 和 intrusive_ptr_release的泛型實(shí)現(xiàn),我們最好有一些直接操作基類(在這里是 reference_counter)的函數(shù)。這樣做的優(yōu)點(diǎn)在于,即使從reference_counter派生的類定義在其它的名字空間,intrusive_ptr_add_ref 和 intrusive_ptr_release 也還可以通過ADL (參數(shù)相關(guān)查找法)找到它們。修改reference_counter的實(shí)現(xiàn)很簡單。
class reference_counter {
int ref_count_;
public:
reference_counter() : ref_count_(0) {}
virtual ~reference_counter() {}
friend void intrusive_ptr_add_ref(reference_counter* p) {
++p->ref_count_;
}
friend void intrusive_ptr_release(reference_counter* p) {
if (--p->ref_count_==0)
delete p;
}
protected:
reference_counter& operator=(const reference_counter&) {
// 無操作
return *this;
}
private:
// 禁止復(fù)制構(gòu)造函數(shù)
reference_counter(const reference_counter&);
};
總的來說,提出一定要用插入式引用計(jì)數(shù)智能指針的情形是不容易的。大多數(shù)情況下,但不是全部情況下,非插入式智能指針都可以解決問題。但是,有一種情形使用插入式引用計(jì)數(shù)會更容易:當(dāng)你需要從一個成員函數(shù)返回 this ,并把它存入另一個智能指針。當(dāng)從一個被非插入式智能指針?biāo)鶕碛械念愋头祷?this時,結(jié)果是有兩個不同的智能指針認(rèn)為它們擁有同一個對象,這意味著它們會在某個時候一起試圖刪除同一個指針。這導(dǎo)致了兩次刪除,結(jié)果可能使你的應(yīng)用程序崩潰。必須有什么辦法可以通知另一個智能指針,這個資源已經(jīng)被一個智能指針?biāo)?,這正好是內(nèi)部引用計(jì)數(shù)器(暗地里)可以做到的。由于 intrusive_ptr 的邏輯不直接對它們所引向的對象的內(nèi)部引用計(jì)數(shù)進(jìn)行操作,這就不會違反所有權(quán)或引用計(jì)數(shù)的完整性。引用計(jì)數(shù)只是被簡單地遞增。
讓我們先看一下一個依賴于boost::shared_ptr來共享資源所有權(quán)的實(shí)現(xiàn)中潛在的問題。它基于本章前面討論enable_shared_from_this時的例子。
#include "boost/shared_ptr.hpp"
class A;
void do_stuff(boost::shared_ptr<A> p) {
// ...
}
class A {
public:
call_do_stuff() {
shared_ptr<A> p(???);
do_stuff(p);
}
};
int main() {
boost::shared_ptr<A> p(new A());
p->call_do_stuff();
}
類A 要調(diào)用函數(shù) do_stuff, 但問題是 do_stuff 要一個 shared_ptr<A>, 而不是一個普通的A指針。因此,在 A::call_do_stuff里,應(yīng)該如何創(chuàng)建 shared_ptr ?現(xiàn)在,讓我們重寫 A ,讓它兼容于 intrusive_ptr, 通過從 reference_counter派生,然后我們再增加一個 do_stuff的重載版本,接受一個 intrusive_ptr<A>類型的參數(shù)。
#include "boost/intrusive_ptr.hpp"
class A;
void do_stuff(boost::intrusive_ptr<A> p) {
// ...
}
void do_stuff(boost::shared_ptr<A> p) {
// ...
}
class A : public reference_counter {
public:
void call_do_stuff() {
do_stuff(this);
}
};
int main() {
boost::intrusive_ptr<A> p(new A());
p->call_do_stuff();
}
如你所見,在這個版本的 A::call_do_stuff里,我們可以直接把 this 傳給需要一個 intrusive_ptr<A>的函數(shù),這是由于 intrusive_ptr的類型轉(zhuǎn)換構(gòu)造函數(shù)。
最后,這里有一個特別的地方:現(xiàn)在 A 可以支持 intrusive_ptr了,我們也可以創(chuàng)建一個包裝intrusive_ptr的shared_ptr,這們我們就可以調(diào)用原來版本的 do_stuff, 它需要一個 shared_ptr<A> 作為參數(shù)。假如你不能控制 do_stuff的源碼,這可能是你要解決的一個非常真實(shí)的問題。這次,還是用定制刪除器的方法來解決,它需要調(diào)用 intrusive_ptr_release. 下面是一個新版本的 A::call_do_stuff.
void call_do_stuff() {
intrusive_ptr_add_ref(this);
boost::shared_ptr<A> p(this,&intrusive_ptr_release<A>);
do_stuff(p);
}
真是一個漂亮的方法。當(dāng)沒有 shared_ptr剩下時,定制的刪除器被調(diào)用,它調(diào)用 intrusive_ptr_release, 遞減A的內(nèi)部引用計(jì)數(shù)。注意,如果 intrusive_ptr_add_ref 和 intrusive_ptr_release 被實(shí)現(xiàn)為直接操作 reference_counter, 你就要這樣來創(chuàng)建 shared_ptr :
boost::shared_ptr<A> p(this,&intrusive_ptr_release);
我們前面提過可以為不同的類型支持不同的引用計(jì)數(shù)。這在集成已有的采用不同引用計(jì)數(shù)機(jī)制的類時是有必要的(例如,第三方的類使用它們自己版本的引用計(jì)數(shù)器)。又或者對于資源的釋放有不同的需求,如調(diào)用delete以外的另一個函數(shù)。如前所述,對 intrusive_ptr_add_ref 和 intrusive_ptr_release 的調(diào)用是非受限的。這意味著在名字查找時要考慮參數(shù)(指針的類型)的作用域,從而這些函數(shù)應(yīng)該與它們操作的類型定義在同一個作用域。如果你在全局名字空間里實(shí)現(xiàn) intrusive_ptr_add_ref 和 intrusive_ptr_release的泛型版本,你就不能在其它名字空間中再創(chuàng)建泛型版本了。例如,如果一個名字空間需要為它的所有類型定義一個特殊的版本,特化版本或重載版本必須提供給每一個類型。否則,全局名字空間中的函數(shù)就會引起歧義。因此在全局名字空間中提供泛型版本不是一個好主意,而在其它名字空間中提供則可以。
既然我們已經(jīng)用基類reference_counter實(shí)現(xiàn)了引用計(jì)數(shù)器,那么在全局名字空間中提供一個接受reference_counter*類型的參數(shù)的普通函數(shù)應(yīng)該是一個好主意。這還可以讓我們在其它名字空間中提供泛型重載版本而不會引起歧義。例如,考慮my_namespace名字空間中的兩個類 another_class 和 derived_class :
namespace my_namespace {
class another_class : public reference_counter {
public:
void call_before_destruction() const {
std::cout <<
"Yes, I'm ready before destruction\n";
}
};
class derived_class : public another_class {};
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) {
t->call_before_destruction();
delete t;
}
}
}
這里,我們實(shí)現(xiàn)了intrusive_ptr_add_ref 和 intrusive_ptr_release的泛型版本。因此我們必須刪掉在全局名字空間中的泛型版本,把它們替換為以一個reference_counter指針為參數(shù)的非模板版本。或者,我們干脆從全局名字空間中刪掉這些函數(shù),也可以避免引起混亂。對于這兩個類 my_namespace::another_class 和 my_namespace::derived_class, 將調(diào)用這個特殊版本(那個調(diào)用了它的參數(shù)的成員函數(shù) call_before_destruction 的版本)。其它類型或者在它們定義所在的名字空間中有相應(yīng)的函數(shù),或者使用全局名字空間中的版本,如果有的話。下面程序示范了這如何工作:
int main() {
boost::intrusive_ptr<my_namespace::another_class>
p1(new my_namespace::another_class());
boost::intrusive_ptr<A>
p2(new good_class());
boost::intrusive_ptr<my_namespace::derived_class>
p3(new my_namespace::derived_class());
}
首先,intrusive_ptr p1 被傳入一個新的 my_namespace::another_class實(shí)例。在解析對 intrusive_ptr_add_ref的調(diào)用時,編譯器會找到 my_namespace里的版本,即 my_namespace::another_class* 參數(shù)所在名字空間。因而,為那個名字空間里的類型所提供的泛型函數(shù)會被正確地調(diào)用。在查找 intrusive_ptr_release時也是同樣。然后,intrusive_ptr p2 被創(chuàng)建并被傳入一個類型A (我們早前創(chuàng)建的那個類型)的指針。那個類型是在全局名字空間里的,所以當(dāng)編譯器試圖去找到函數(shù) intrusive_ptr_add_ref的最佳匹配時,它只會找到一個版本,即接受reference_counter指針類型的那個版本(你應(yīng)該記得我們已經(jīng)從全局名字空間中刪掉了泛型版本)。因?yàn)?A 公共繼承自 reference_counter, 通過隱式類型轉(zhuǎn)換就可以進(jìn)行正確的調(diào)用。最后,my_namespace 里的泛型版本被用于類 my_namespace::derived_class; 這與 another_class例子中的查找是一樣的。
這里最重要的教訓(xùn)是,在實(shí)現(xiàn)函數(shù) intrusive_ptr_add_ref 和 intrusive_ptr_release時,它們應(yīng)該總是定義在它們操作的類型所在的名字空間里。從設(shè)計(jì)的角度來看,這也是完美的,把相關(guān)的東西放在一起,這有助于確??偸钦{(diào)用正確的版本,而不用擔(dān)心是否有多個不同的實(shí)現(xiàn)可供選擇。
在多數(shù)情況下,你不應(yīng)該使用 boost::intrusive_ptr, 因?yàn)楣蚕硭袡?quán)的功能已在 boost::shared_ptr中提供,而且非插入式智能指針比插入式智能指針更靈活。但是,有時候也會需要插入式的引用計(jì)數(shù),可能是由于舊的代碼,或者是為了與第三方的類進(jìn)行集成。當(dāng)有這種需要時,可以用 intrusive_ptr ,它具有與其它Boost智能指針相同的語義。如果你使用過其它的Boost智能指針,你就會發(fā)現(xiàn)不論是否插入式的,所有智能指針都有一致的接口。使用intrusive_ptr的類必須可以提供引用計(jì)數(shù)。ntrusive_ptr 通過調(diào)用兩個函數(shù),intrusive_ptr_add_ref 和 intrusive_ptr_release來管理引用計(jì)數(shù);這兩個函數(shù)必須正確地操作插入式的引用計(jì)數(shù),以保證 intrusive_ptr正確工作。在使用intrusive_ptr的類中已經(jīng)內(nèi)置有引用計(jì)數(shù)的情況下,實(shí)現(xiàn)對intrusive_ptr的支持就是實(shí)現(xiàn)這兩個函數(shù)。有些情況下,可以創(chuàng)建這兩個函數(shù)的參數(shù)化版本,然后對所有帶插入式引用計(jì)數(shù)的類型使用相同的實(shí)現(xiàn)。多數(shù)時候,聲明這兩個函數(shù)的最好的地方就是它們所支持的類型所在的名字空間。
在以下情況時使用 intrusive_ptr :
你需要把 this 當(dāng)作智能指針來使用。
已有代碼使用或提供了插入式的引用計(jì)數(shù)。
智能指針的大小必須與裸指針的大小相等。