auto_ptr、shared_ptr、weak_ptr、scoped_ptr用法小結

auto_ptr
 
auto_ptr是現在標準庫裏面一個輕量級的智能指針的實現,存在於頭文件 memory中,之所以說它是輕量級,是因爲它只有一個成員變量(擁有對象的指針),相關的調用開銷也非常小。
 

下面的代碼來自於VC++ 8.0裏面的源碼:

 
 
裏面有個auto_ptr_ref的數據結構,我們可以把它忽略,這個只是內部使用的代理結構,用於一些隱式的const變化,我們客戶端代碼通常不會直接使用到它。
 
我們可以看到除了構造函數,拷貝構造函數,賦值函數,析構函數和兩個重載操作符(*,->)外,還有get,release和reset三個函數,它們的作用分別是:
 
<!--[if !supportLists]-->1. <!--[endif]-->get,獲得內部對象的指針
<!--[if !supportLists]-->2. <!--[endif]-->release,放棄內部對象的所有權,將內部指針置爲空
<!--[if !supportLists]-->3. <!--[endif]-->reset,銷燬內部對象並接受新的對象的所有權(如果使用缺省參數的話,也就是沒有任何對象的所有權)
 

下面的例程來自Exceptional C++,Item 37:

 
// Example 2: Using an auto_ptr
//
void g()
{
     T
* pt1 = new T;
 
    // right now, we own the allocated object
 
    // pass ownership to an auto_ptr
     auto_ptr<T> pt2( pt1 );
 
    // use the auto_ptr the same way
 
    // we'd use a simple pointer
     *pt2 = 12;       // same as "*pt1 = 12;"

     pt2
->SomeFunc(); // same as "pt1->SomeFunc();"
     
// use get() to see the pointer value
     assert( pt1 == pt2.get() );
     
// use release() to take back ownership
     T* pt3 = pt2.release();
     
// delete the object ourselves, since now
     
// no auto_ptr owns it any more
     delete pt3;

}
 // pt2 doesn't own any pointer, and so won't
 
// try to delete it... OK, no double delete
 

// Example 3: Using reset()
//
void h()
{
     auto_ptr
<T> pt( new T(1) );
     pt.reset( 
new T(2) );
    
// deletes the first T that was
    
// allocated with "new T(1)"

}
 // finally, pt goes out of scope and
 
// the second T is also deleted

 

 
 
從上面的例子來看,auto_ptr的使用很簡單,通過構造函數擁有一個動態分配對象的所有權,然後就可以被當作對象指針來使用,當auto_ptr對象被銷燬的時候,它也會自動銷燬自己擁有所有權的對象(嗯,標準的RAAI做法),release可以用來手動放棄所有權,reset可用於手動銷燬內部對象。
 
但實際上,auto_ptr是一個相當容易被誤用並且在實際中常常被誤用的類。原因是由於它的對象所有權佔用的特性和它非平凡的拷貝行爲。
 
auto_ptr的對象所有權是獨佔性的!
 
這決定了不可能有兩個auto_ptr對象同時擁有同一動態對象的所有權,從而也導致了auto_ptr的拷貝行爲是非對等的,其中伴隨着對象所有權的轉移。
 
我們仔細觀察auto_ptr的源碼就會發現拷貝構造和賦值操作符所接受的參數類型都是非const的引用類型(auto_ptr<_Ty>& ),而不是我們一般應該使用的const引用類型,查看源碼我們會發現:
 
         auto_ptr(auto_ptr<_Ty>& _Right) _THROW0()
                 : _Myptr(_Right.release())

                 
{        // construct by assuming pointer from _Right auto_ptr
                 }

 
         template
<class _Other>
                 auto_ptr
<_Ty>& operator=(auto_ptr<_Other>& _Right) _THROW0()

                 
{        // assign compatible _Right (assume pointer)
                 reset(_Right.release());
                 
return (*this);
                 }

 

 
拷貝過程中被拷貝的對象(_Right)都會被調用release來放棄所包括的動態對象的所有權,動態對象的所有權被轉移了,新的auto_ptr獨佔了動態對象的所有權。也就是說被拷貝對象在拷貝過程中會被修改,拷貝物與被拷貝物之間是非等價的。這意味着如下的代碼是錯誤的(例程來自 Exceptional C++ Item 37):
 
// Example 6: Never try to do work through

//            a non-owning auto_ptr
//
void f()
{
     auto_ptr
<T> pt1( new T );
     auto_ptr
<T> pt2;

     pt2 
= pt1; // now pt2 owns the pointer, and

              
// pt1 does not
     pt1->DoSomething();

              
// error: following a null pointer
}

 

 
同時也不要將auto_ptr放進標準庫的容器中,否則在標準庫容器無準備的拷貝行爲中(標準庫容器需要的拷貝行爲是等價的),會導致難以發覺的錯誤。
auto_ptr特殊的拷貝行爲使得使用它來遠距離傳遞動態對象變成了一件十分危險的行爲,在傳遞的過程中,一不小心就會留下一些實際爲空但程序本身卻缺少這樣認知的auto_ptr對象。
 
簡單的說來,auto_ptr適合用來管理生命週期比較短或者不會被遠距離傳遞的動態對象,使用auto_ptr來管理動態分配對象,最好是侷限於某個函數內部或者是某個類的內部。也就是說,動態對象的產生,使用和銷燬的全過程是處於一個小的受控的範圍,而不會在其中加入一些適應未來時態的擴展。


auto_ptr的幾點注意事項:
1、auto_ptr不能共享所有權
2、auto_ptr不能指向數組
3、auto_ptr不能作爲容器的成員
4、不能通過複製操作來初始化auto_ptr
std::auto_ptr<int> p(new int(42)); //OK
std::atuo_ptr<int>p = new int(42);//Error
這是因爲auto_ptr的構造函數被定義了explicit
5、不要把auto_ptr放入容器


shared_ptr

shared_ptr是Boost庫所提供的一個智能指針的實現,正如其名字所蘊意的一樣:
 

An important goal of shared_ptr is to provide a standard shared-ownership pointer.

shared_ptr的一個重要目的就是爲了提供一個標準的共享所有權的智能指針。
                                                        —— Boost庫文檔
 
沒錯,shared_ptr就是爲了解決auto_ptr在對象所有權上的侷限性(auto_ptr是獨佔的),在使用引用計數的機制上提供了可以共享所有權的智能指針,當然這不會沒有任何額外的代價……
 
首先一個shared_ptr對象除了包括一個所擁有對象的指針(px)外,還必須包括一個引用計數代理對象(shared_count)的指針(pn)。而這個引用計數代理對象包括一個真正的多態的引用計數對象(sp_counted_base)的指針(_pi),真正的引用計數對象在使用VC編譯器的情況下包括一個虛表,一個虛表指針,和兩個計數器。
 
下圖中result是一個shared_ptr對象,我們可以清楚看到它展開後所包含的數據:
 

<!--[if !vml]--><!--[endif]-->

 
假設我們有多個(5個以上)shared_ptr共享一個動態對象,那麼每個shared_ptr的開銷比起只使用原生指針的開銷大概在3,4倍左右(這還是理想狀況,忽略了動態分配帶來的俑餘開銷)。如果只有一個shared_ptr獨佔動態對象,空間上開銷更是高度十數倍!而auto_ptr的開銷只是使用原生指針的兩倍。
 
時間上的開銷主要在初始化和拷貝操作上,*和->操作符重載的開銷跟auto_ptr是一樣的。
 
當然開銷並不是我們不使用shared_ptr的理由,永遠不要進行不成熟的優化,直到性能分析器告訴你這一點,這是Hurb提出的明智的建議。以上的說明只是爲了讓你瞭解強大的功能背後總是伴隨着更多的開銷,shared_ptr應該被使用,但是也不要過於濫用,特別是在一些auto_ptr更擅長的地方。
 
下面是shared_ptr的類型定義:
 
 template<class T> class shared_ptr {
 

    
public:
 

      typedef T element_type;
 

      shared_ptr(); 
// never throws

      template
<class Y> explicit shared_ptr(Y * p);

      template
<class Y, class D> shared_ptr(Y * p, D d);

      
~shared_ptr(); // never throws
 

      shared_ptr(shared_ptr 
const & r); // never throws

      template
<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws

      template
<class Y> explicit shared_ptr(weak_ptr<Y> const & r);

      template
<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
 

      shared_ptr 
& operator=(shared_ptr const & r); // never throws 

      template
<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws

      template
<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
 

      
void reset(); // never throws

      template
<class Y> void reset(Y * p);

      template
<class Y, class D> void reset(Y * p, D d);
 

      T 
& operator*() const// never throws

      T 
* operator->() const// never throws

      T 
* get() const// never throws
 

      
bool unique() const// never throws

      
long use_count() const// never throws
 

      
operator unspecified-bool-type() const// never throws
 

      
void swap(shared_ptr & b); // never throws
 }
;

 
大多數成員函數都跟auto_ptr類似,但是沒有了release(請看註釋),reset用來放棄所擁有對象的所有權或擁有對象的變更,會引起原有對象的引用計數的減少。

 
Note:
 
Boost文檔裏面的QA說明了爲什麼不提供release函數
 

Q. Why doesn
't shared_ptr provide a release() function?

A. shared_ptr cannot give away ownership unless it
's unique() because the other copy will still destroy
 the object.

Consider:
shared_ptr
<int> a(new int);

shared_ptr
<int> b(a); // a.use_count() == b.use_count() == 2
 
int * p = a.release();
 

// Who owns p now? b will still call delete on it in its destructor.
Furthermore, the pointer returned by release() would be difficult to deallocate reliably, 
as the source shared_ptr could have been created with a custom deleter.

 
use_count返回引用計數的個數,unique擁於確認是否是獨佔所有權(use_count爲1),swap用於交換兩個shared_ptr對象(即交換所擁有的對象),有一個bool類型轉換操作符使得shared_ptr可用於需要的bool類型的語境下,比如我們通常用if(pointer)來判斷某個指針是否爲空。
 
Boost庫裏面有很多shared_ptr的使用例程,文檔裏面也列舉了許許多多shared_ptr的用途,其中最有用也最常用的莫過於傳遞動態分配對象,有了引用計數機制,我們現在可以安全地將動態分配的對象包裹在shared_ptr裏面跨越模塊,跨越線程的邊界傳遞。shared_ptr爲我們自動管理對象的生命週期,嗯,C++也可以體會到Java裏面使用引用的美妙之處了。
 

另外,還記得Effective C++裏面(或者其它的C++書籍),Scott Meyer告訴你的:在一個由多個模塊組成的系統裏面,一個模塊不用試圖自己去釋放另外一個模塊分配的資源,而應該遵循誰分配誰釋放的原則。正確的原則但是有時難免有時讓人忽略(過於繁瑣),將資源包裝在shared_ptr裏面傳遞,而shared_ptr保證了在資源不再被擁有的時候,產生資源的模塊的delete語句會被調用。

 
shared_ptr是可以拷貝和賦值的,拷貝行爲也是等價的,並且可以被比較,這意味這它可被放入標準庫的一般容器(vector,list)和關聯容器中(map)。
 
shared_ptr可以用來容納多態對象,比如所下面的例子:
 
class Base
{
}


class Derived : public Base
{
}

 

shared_ptr
<Base> sp_base(new Derived);

 
 
甚至shared_ptr也具備多態的行爲:
 
Derived* pd = new Derived;
 
shared_ptr
<Derived> sp_derived(pd);
shared_ptr
<Base> sp_base2(sp_derived);
 
上面的語句是合法的,shared_ptr會完成所需的類型轉換,當shared_ptr的模版參數Base的確是Derived的基類的時候。
 
 
最後是一個小小的提醒,無論是使用auto_ptr還是shared_ptr,都永遠不要寫這樣的代碼:
 
A* pa = new A;
xxx_ptr
<A> ptr_a_1(pa);
xxx_ptr
<A> ptr_a_2(pa);

 
很明顯,在ptr_a_1和ptr_a_2生命週期結束的時候都會去刪除pa,pa被刪除了兩次,這肯定會引起你程序的崩潰。

可能有人會疑問,假如使用shared_ptr,第二次引用pa的時候,計數會加一。知道ptr_a_1和ptr_a_2的生命週期全部結束時纔會刪除,怎麼會崩潰呢。

實際上,shared_ptr的構造函數是不一樣的

template <class Y> explicit shared_ptr(Y* p);

這個構造函數獲得給定指針p的所有權。參數 p 必須是指向Y 的有效指針。構造後引用計數設爲1。唯一從這個構造函數拋出的異常是std::bad_alloc (僅在一種很罕見的情況下發生,即不能獲得引用計數器所需的自由空間)。



shared_ptr(const shared_ptr& r);

r中保存的資源被新構造的shared_ptr所共享,引用計數加一。這個構造函數不會拋出異常。


因此,直接用指針作爲構造形參傳入shared_ptr,計數是不會加一的,當它生命週期結束時,就會去刪除指針,因此,上面的例子實際上刪除了兩次。

當然,這個誤用的例子比較明顯,但是在某種情況下,可能會一不小心就寫出如下的代碼(嗯,我承認我的確做過這樣的事情):
 
void DoSomething(xxx_ptr<A>)
{

         
//do something
}

 
class A
{
         doSomething()
         
{

                 xxx_ptr
<A> ptr_a(this);
                 DoSomething(ptr_a);
         }

}
;
 
int main()
{

         A a;
         a.doSomething();

         
//continue do something with a, but it was already destory
}

 
在函數a.doSomething()裏面發生了什麼事情,它爲了調用DoSomething所以不得不把自己包裝成一個xxx_ptr,但是忘記在函數結束的時候,xxx ptr_a被銷燬的同時也銷燬了自己,程序或者立刻崩潰或者在下面的某個時間點上崩潰!
 
所以你在使用智能指針做爲函數參數的時候請小心這樣的誤用,有時候使用智能指針作爲函數參數不一定是一個好注意。比如請遵循下面的建議,請不要在屬於類型A的接口的一部分的非成員函數或者跟A有緊密聯繫的輔助函數裏面使用xxx_ptr<A>作爲函數的參數類型。



weak_ptr


weak_ptr是爲配合shared_ptr而引入的一種智能指針來協助shared_ptr工作,它可以從一個shared_ptr或另一個weak_ptr對象構造,它的構造和析構不會引起引用記數的增加或減少。沒有重載*和->但可以使用lock獲得一個可用的shared_ptr對象
C++代碼 複製代碼 收藏代碼
  1. template<class T> class weak_ptr   
  2. {   
  3. public:   
  4.     weak_ptr();   
  5.   
  6.     template<class Y> weak_ptr(shared_ptr<Y> const & r);   
  7.     weak_ptr(weak_ptr const & r);   
  8.   
  9.     ~weak_ptr();   
  10.     weak_ptr & operator=(weak_ptr const & r);   
  11.   
  12.     long use_count()const;   
  13.     //判斷是否過期   
  14.     bool expired()const;   
  15.     //得到一個空的或者被協助的shared_ptr   
  16.     shared_ptr<T> lock()const;   
  17.   
  18.     void reset();   
  19.     void swap(weak_ptr<T> & b);   
  20. };  
  1. template<class T> class weak_ptr  
  2. {  
  3. public:  
  4.     weak_ptr();  
  5.   
  6.     template<class Y> weak_ptr(shared_ptr<Y> const & r);  
  7.     weak_ptr(weak_ptr const & r);  
  8.   
  9.     ~weak_ptr();  
  10.     weak_ptr & operator=(weak_ptr const & r);  
  11.   
  12.     long use_count()const;  
  13.     //判斷是否過期  
  14.     bool expired()const;  
  15.     //得到一個空的或者被協助的shared_ptr  
  16.     shared_ptr<T> lock()const;  
  17.   
  18.     void reset();  
  19.     void swap(weak_ptr<T> & b);  
  20. };  


C++代碼 複製代碼 收藏代碼
  1. #include<iostream>   
  2. #include<boost/weak_ptr.hpp>   
  3. #include<boost/shared_ptr.hpp>   
  4. using namespace std;   
  5. using namespace boost;   
  6.   
  7. int main()   
  8. {   
  9.     shared_ptr<int> p(new int(10));   
  10.     weak_ptr<int> w(p);   
  11.     while(!w.expired()){   
  12.         cout << w.use_count() << endl;   
  13.         shared_ptr<int> t = w.lock();   
  14.         cout << *t << endl;   
  15.         cout << w.use_count() << endl;   
  16.         if(w.use_count()==2){   
  17.             break;   
  18.         }   
  19.     }   
  20.     w.reset();   
  21.     cout << w.expired() << endl;   
  22. }   
  23.   
  24. 1   
  25. 10   
  26. 2   
  27. 1  
  1. #include<iostream>  
  2. #include<boost/weak_ptr.hpp>  
  3. #include<boost/shared_ptr.hpp>  
  4. using namespace std;  
  5. using namespace boost;  
  6.   
  7. int main()  
  8. {  
  9.     shared_ptr<int> p(new int(10));  
  10.     weak_ptr<int> w(p);  
  11.     while(!w.expired()){  
  12.         cout << w.use_count() << endl;  
  13.         shared_ptr<int> t = w.lock();  
  14.         cout << *t << endl;  
  15.         cout << w.use_count() << endl;  
  16.         if(w.use_count()==2){  
  17.             break;  
  18.         }  
  19.     }  
  20.     w.reset();  
  21.     cout << w.expired() << endl;  
  22. }  
  23.   
  24. 1  
  25. 10  
  26. 2  
  27. 1  


weak_ptr的一個重要用途是通過lock獲得this指針的shared_ptr,使對象自己能夠生產shared_ptr來管理自己,但助手類enable_shared_from_this的shared_from_this會返回this的shared_ptr,只需要讓想被shared_ptr管理的類從它繼承即可

C++代碼 複製代碼 收藏代碼
  1. #include<boost/enable_shared_from_this.hpp>   
  2. #include<boost/make_shared.hpp>   
  3. #include<iostream>   
  4. using namespace boost;   
  5. using namespace std;   
  6.   
  7. class testWeak:public enable_shared_from_this<testWeak>{//  
  8. public:   
  9.     int i;   
  10.     testWeak(int ii):i(ii){}   
  11.     void print(){   
  12.         cout << i << endl;   
  13.     }   
  14. };   
  15.   
  16. int main()   
  17. {   
  18.     shared_ptr<testWeak> sp = make_shared<testWeak>(100);   
  19.     shared_ptr<testWeak> tw = sp->shared_from_this();   
  20.     tw->print();   
  21. }   

循環引用

 

引用計數是一種便利的內存管理機制,但它有一個很大的缺點,那就是不能管理循環引用的對象。一個簡單的例子如下:

 

Cpp代碼 複製代碼 收藏代碼
  1. #include <string>   
  2. #include <iostream>   
  3. #include <boost/shared_ptr.hpp>   
  4. #include <boost/weak_ptr.hpp>   
  5.   
  6. class parent;   
  7. class children;   
  8.   
  9. typedef boost::shared_ptr<parent> parent_ptr;   
  10. typedef boost::shared_ptr<children> children_ptr;   
  11.   
  12. class parent   
  13. {   
  14. public:   
  15.     ~parent() { std::cout <<"destroying parent\n"; }   
  16.   
  17. public:   
  18.     children_ptr children;   
  19. };   
  20.   
  21. class children   
  22. {   
  23. public:   
  24.     ~children() { std::cout <<"destroying children\n"; }   
  25.   
  26. public:   
  27.     parent_ptr parent;   
  28. };   
  29.   
  30.   
  31. void test()   
  32. {   
  33.     parent_ptr father(new parent());   
  34.     children_ptr son(new children);   
  35.   
  36.     father->children = son;   
  37.     son->parent = father;   
  38. }   
  39.   
  40. void main()   
  41. {   
  42.     std::cout<<"begin test...\n";   
  43.     test();   
  44.     std::cout<<"end test.\n";   
  45. }  
  1. #include <string>  
  2. #include <iostream>  
  3. #include <boost/shared_ptr.hpp>  
  4. #include <boost/weak_ptr.hpp>  
  5.   
  6. class parent;  
  7. class children;  
  8.   
  9. typedef boost::shared_ptr<parent> parent_ptr;  
  10. typedef boost::shared_ptr<children> children_ptr;  
  11.   
  12. class parent  
  13. {  
  14. public:  
  15.     ~parent() { std::cout <<"destroying parent\n"; }  
  16.   
  17. public:  
  18.     children_ptr children;  
  19. };  
  20.   
  21. class children  
  22. {  
  23. public:  
  24.     ~children() { std::cout <<"destroying children\n"; }  
  25.   
  26. public:  
  27.     parent_ptr parent;  
  28. };  
  29.   
  30.   
  31. void test()  
  32. {  
  33.     parent_ptr father(new parent());  
  34.     children_ptr son(new children);  
  35.   
  36.     father->children = son;  
  37.     son->parent = father;  
  38. }  
  39.   
  40. void main()  
  41. {  
  42.     std::cout<<"begin test...\n";  
  43.     test();  
  44.     std::cout<<"end test.\n";  
  45. }  

 

    運行該程序可以看到,即使退出了test函數後,由於parent和children對象互相引用,它們的引用計數都是1,不能自動釋放,並且此時這兩個對象再無法訪問到。這就引起了c++中那臭名昭著的內存泄漏。

 

一般來講,解除這種循環引用有下面有三種可行的方法:

  • 當只剩下最後一個引用的時候需要手動打破循環引用釋放對象。
  • 當parent的生存期超過children的生存期的時候,children改爲使用一個普通指針指向parent。
  • 使用弱引用的智能指針打破這種循環引用。

雖然這三種方法都可行,但方法1和方法2都需要程序員手動控制,麻煩且容易出錯。這裏主要介紹一下第三種方法和boost中的弱引用的智能指針boost::weak_ptr。

 

 

強引用和弱引用

 

對於強引用來說,如果被引用的對象還活着,那麼這個強引用也存在(就是說,當至少有一個強引用,那麼這個對象就不能被釋放)。boost::share_ptr就是強引用。

 

相對而言,對於弱引用來說,當引用的對象活着的時候弱引用不一定存在。僅僅是當它存在的時候的一個引用。弱引用並不修改該對象的引用計數,這意味這弱引用它並不對對象的內存進行管理,在功能上類似於普通指針,然而一個比較大的區別是,弱引用能檢測到所管理的對象是否已經被釋放,從而避免訪問非法內存。

 

 

boost::weak_ptr

 

boost::weak_ptr<T>是boost提供的一個弱引用的智能指針,它的聲明可以簡化如下:

 

Cpp代碼 複製代碼 收藏代碼
  1. namespace boost {   
  2.   
  3.     template<typename T> class weak_ptr {   
  4.     public:   
  5.         template <typename Y>   
  6.         weak_ptr(const shared_ptr<Y>& r);   
  7.   
  8.         weak_ptr(const weak_ptr& r);   
  9.   
  10.         ~weak_ptr();   
  11.   
  12.         T* get() const;    
  13.         bool expired() const;    
  14.         shared_ptr<T> lock() const;   
  15.     };    
  16. }  
  1. namespace boost {  
  2.   
  3.     template<typename T> class weak_ptr {  
  4.     public:  
  5.         template <typename Y>  
  6.         weak_ptr(const shared_ptr<Y>& r);  
  7.   
  8.         weak_ptr(const weak_ptr& r);  
  9.   
  10.         ~weak_ptr();  
  11.   
  12.         T* get() const;   
  13.         bool expired() const;   
  14.         shared_ptr<T> lock() const;  
  15.     };   
  16. }  

 

    可以看到,boost::weak_ptr必須從一個boost::shared_ptr或另一個boost::weak_ptr轉換而來。這也說明,進行該對象的內存管理的是那個強引用的boost::shared_ptr。boost::weak_ptr只是提供了對管理對象的一個訪問手段。

 

boost::weak_ptr除了對所管理對象的基本訪問功能(通過get()函數)外,還有兩個常用的功能函數:expired()用於檢測所管理的對象是否已經釋放;lock()用於獲取所管理的對象的強引用指針。

 

 

通過boost::weak_ptr來打破循環引用

 

由於弱引用不更改引用計數,類似普通指針,只要把循環引用的一方使用弱引用,即可解除循環引用。對於上面的那個例子來說,只要把children的定義改爲如下方式,即可解除循環引用:

 

Cpp代碼 複製代碼 收藏代碼
  1. class children   
  2. {   
  3. public:   
  4.     ~children() { std::cout <<"destroying children\n"; }   
  5.   
  6. public:   
  7.     boost::weak_ptr<parent> parent;   
  8. };  
  1. class children  
  2. {  
  3. public:  
  4.     ~children() { std::cout <<"destroying children\n"; }  
  5.   
  6. public:  
  7.     boost::weak_ptr<parent> parent;  
  8. };  

 

   最後值得一提的是,雖然通過弱引用指針可以有效的解除循環引用,但這種方式必須在程序員能預見會出現循環引用的情況下才能使用,也可以是說這個僅僅是一種編譯期的解決方案,如果程序在運行過程中出現了循環引用,還是會造成內存泄漏的。因此,不要認爲只要使用了智能指針便能杜絕內存泄漏。畢竟,對於C++來說,由於沒有垃圾回收機制,內存泄漏對每一個程序員來說都是一個非常頭痛的問題。


scoped_ptr


頭文件: "boost/scoped_ptr.hpp"

boost::scoped_ptr 用於確保動態分配的對象能夠被正確地刪除。scoped_ptr 有着與std::auto_ptr類似的特性,而最大的區別在於它不能轉讓所有權而auto_ptr可以。事實上,scoped_ptr永遠不能被複制或被賦值scoped_ptr 擁有它所指向的資源的所有權,並永遠不會放棄這個所有權。scoped_ptr的這種特性提升了我們的代碼的表現,我們可以根據需要選擇最合適的智能指針(scoped_ptr 或 auto_ptr)。

要決定使用std::auto_ptr還是boost::scoped_ptr, 就要考慮轉移所有權是不是你想要的智能指針的一個特性。如果不是,就用scoped_ptr. 它是一種輕量級的智能指針;使用它不會使你的程序變大或變慢。它只會讓你的代碼更安全,更好維護。

下面是scoped_ptr的摘要,以及其成員的簡要描述:

namespace boost {

  template<typename T> class scoped_ptr : noncopyable {
  public:
    explicit scoped_ptr(T* p = 0); 
    ~scoped_ptr(); 

    void reset(T* p = 0); 

    T& operator*() const; 
    T* operator->() const; 
    T* get() const; 
   
    void swap(scoped_ptr& b); 
  };

  template<typename T> 
    void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); 
}

成員函數

explicit scoped_ptr(T* p=0)

構造函數,存儲p的一份拷貝。注意,p 必須是用operator new分配的,或者是null. 在構造的時候,不要求T必須是一個完整的類型。當指針p是調用某個分配函數的結果而不是直接調用new得到的時候很有用:因爲這個類型不必是完整的,只需要類型T的一個前向聲明就可以了。這個構造函數不會拋出異常。

~scoped_ptr()

刪除被指物。類型T在被銷燬時必須是一個完整的類型。如果scoped_ptr在它被析構時並沒有保存資源,它就什麼都不做。這個析構函數不會拋出異常。

void reset(T* p=0);

重置一個 scoped_ptr 就是刪除它已保存的指針,如果它有的話,並重新保存 p. 通常,資源的生存期管理應該完全由scoped_ptr自己處理,但是在極少數時候,資源需要在scoped_ptr的析構之前釋放,或者scoped_ptr要處理它原有資源之外的另外一個資源。這時,就可以用reset但一定要儘量少用它。(過多地使用它通常表示有設計方面的問題) 這個函數不會拋出異常。

T& operator*() const;

返回一個到被保存指針指向的對象的引用。由於不允許空的引用,所以解引用一個擁有空指針的scoped_ptr將導致未定義行爲。如果不能肯定所含指針是否有效,就用函數get替代解引用。這個函數不會拋出異常。

T* operator->() const;

返回保存的指針。如果保存的指針爲空,則調用這個函數會導致未定義行爲。如果不能肯定指針是否空的,最好使用函數get。這個函數不會拋出異常。

T* get() const;

返回保存的指針。應該小心地使用get,因爲它可以直接操作裸指針。但是,get使得你可以測試保存的指針是否爲空。這個函數不會拋出異常。get通常在調用那些需要裸指針的函數時使用。

operator unspecified_bool_type() const

返回scoped_ptr是否爲非空。返回值的類型是未指明的,但這個類型可被用於Boolean的上下文中。在if語句中最好使用這個類型轉換函數,而不要用get去測試scoped_ptr的有效性

void swap(scoped_ptr& b)

交換兩個scoped_ptr的內容。這個函數不會拋出異常。

普通函數

template<typename T> void swap(scoped_ptr<T>& a,scoped_ptr<T>& b)

這個函數提供了交換兩個scoped pointer的內容的更好的方法。之所以說它更好,是因爲 swap(scoped1,scoped2)可以更廣泛地用於很多指針類型,包括裸指針和第三方的智能指針。scoped1.swap(scoped2) 則只能用於它的定義所在的智能指針,而不能用於裸指針。

你可爲那些不夠智能,沒有提供它們自己的交換函數的智能指針創建你的普通swap函數。

用法

scoped_ptr的用法與普通的指針沒什麼區別;最大的差別在於你不必再記得在指針上調用delete,還有複製是不允許的。典型的指針操作(operator* 和 operator->)都被重載了,並提供了和裸指針一樣的語法。用scoped_ptr和用裸指針一樣快,也沒有大小上的增加,因此它們可以廣泛使用。使用boost::scoped_ptr時,包含頭文件"boost/scoped_ptr.hpp". 在聲明一個scoped_ptr時,用被指物的類型來指定類模板的參數。例如,以下是一個包含std::string指針的scoped_ptr

boost::scoped_ptr<std::string> p(new std::string("Hello"));

scoped_ptr被銷燬時,它對它所擁有的指針調用delete 。


 

scoped_ptr 不同於 const auto_ptr

留心的讀者可能已經注意到auto_ptr可以幾乎象scoped_ptr一樣地工作,只要把auto_ptr聲明爲const

const auto_ptr<A> no_transfer_of_ownership(new A);

它們很接近,但不是一樣。最大的區別在於scoped_ptr可以被reset, 在需要時可以刪除並替換被指物。而對於const auto_ptr這是不可能的。另一個小一點的區別是,它們的名字不同:儘管const auto_ptr意思上和scoped_ptr一樣,但它更冗長,也更不明顯。當你的詞典裏有了scoped_ptr,你就應該使用它,因爲它可以更清楚地表明你的意圖。如果你想說一個資源是要被限制在作用域裏的,並且不應該有辦法可以放棄它的所有權,你就應該用 boost::scoped_ptr.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章