下面的代碼來自於VC++ 8.0裏面的源碼:
下面的例程來自Exceptional C++,Item 37:
//
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
: _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);
}
// 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
}
1、auto_ptr不能共享所有權
An important goal of shared_ptr is to provide a standard shared-ownership pointer.
<!--[if !vml]--><!--[endif]-->
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
};
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.
另外,還記得Effective C++裏面(或者其它的C++書籍),Scott Meyer告訴你的:在一個由多個模塊組成的系統裏面,一個模塊不用試圖自己去釋放另外一個模塊分配的資源,而應該遵循誰分配誰釋放的原則。正確的原則但是有時難免有時讓人忽略(過於繁瑣),將資源包裝在shared_ptr裏面傳遞,而shared_ptr保證了在資源不再被擁有的時候,產生資源的模塊的delete語句會被調用。
{
}
class Derived : public Base
{
}
shared_ptr<Base> sp_base(new Derived);
shared_ptr<Derived> sp_derived(pd);
shared_ptr<Base> sp_base2(sp_derived);
xxx_ptr<A> ptr_a_1(pa);
xxx_ptr<A> ptr_a_2(pa);
template <class Y> explicit shared_ptr(Y* p);
這個構造函數獲得給定指針p的所有權。參數 p 必須是指向Y 的有效指針。構造後引用計數設爲1。唯一從這個構造函數拋出的異常是std::bad_alloc (僅在一種很罕見的情況下發生,即不能獲得引用計數器所需的自由空間)。
shared_ptr(const shared_ptr& r);
r中保存的資源被新構造的shared_ptr所共享,引用計數加一。這個構造函數不會拋出異常。
{
//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
}
weak_ptr
- template<class T> class weak_ptr
- {
- public:
- weak_ptr();
- template<class Y> weak_ptr(shared_ptr<Y> const & r);
- weak_ptr(weak_ptr const & r);
- ~weak_ptr();
- weak_ptr & operator=(weak_ptr const & r);
- long use_count()const;
- //判斷是否過期
- bool expired()const;
- //得到一個空的或者被協助的shared_ptr
- shared_ptr<T> lock()const;
- void reset();
- void swap(weak_ptr<T> & b);
- };
- template<class T> class weak_ptr
- {
- public:
- weak_ptr();
- template<class Y> weak_ptr(shared_ptr<Y> const & r);
- weak_ptr(weak_ptr const & r);
- ~weak_ptr();
- weak_ptr & operator=(weak_ptr const & r);
- long use_count()const;
- //判斷是否過期
- bool expired()const;
- //得到一個空的或者被協助的shared_ptr
- shared_ptr<T> lock()const;
- void reset();
- void swap(weak_ptr<T> & b);
- };
- #include<iostream>
- #include<boost/weak_ptr.hpp>
- #include<boost/shared_ptr.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- shared_ptr<int> p(new int(10));
- weak_ptr<int> w(p);
- while(!w.expired()){
- cout << w.use_count() << endl;
- shared_ptr<int> t = w.lock();
- cout << *t << endl;
- cout << w.use_count() << endl;
- if(w.use_count()==2){
- break;
- }
- }
- w.reset();
- cout << w.expired() << endl;
- }
- 1
- 10
- 2
- 1
- #include<iostream>
- #include<boost/weak_ptr.hpp>
- #include<boost/shared_ptr.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- shared_ptr<int> p(new int(10));
- weak_ptr<int> w(p);
- while(!w.expired()){
- cout << w.use_count() << endl;
- shared_ptr<int> t = w.lock();
- cout << *t << endl;
- cout << w.use_count() << endl;
- if(w.use_count()==2){
- break;
- }
- }
- w.reset();
- cout << w.expired() << endl;
- }
- 1
- 10
- 2
- 1
weak_ptr的一個重要用途是通過lock獲得this指針的shared_ptr,使對象自己能夠生產shared_ptr來管理自己,但助手類enable_shared_from_this的shared_from_this會返回this的shared_ptr,只需要讓想被shared_ptr管理的類從它繼承即可
- #include<boost/enable_shared_from_this.hpp>
- #include<boost/make_shared.hpp>
- #include<iostream>
- using namespace boost;
- using namespace std;
- class testWeak:public enable_shared_from_this<testWeak>{//
- public:
- int i;
- testWeak(int ii):i(ii){}
- void print(){
- cout << i << endl;
- }
- };
- int main()
- {
- shared_ptr<testWeak> sp = make_shared<testWeak>(100);
- shared_ptr<testWeak> tw = sp->shared_from_this();
- tw->print();
- }
循環引用
引用計數是一種便利的內存管理機制,但它有一個很大的缺點,那就是不能管理循環引用的對象。一個簡單的例子如下:
- #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";
- }
- #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";
- }
運行該程序可以看到,即使退出了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提供的一個弱引用的智能指針,它的聲明可以簡化如下:
- 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;
- };
- }
- 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::shared_ptr或另一個boost::weak_ptr轉換而來。這也說明,進行該對象的內存管理的是那個強引用的boost::shared_ptr。boost::weak_ptr只是提供了對管理對象的一個訪問手段。
boost::weak_ptr除了對所管理對象的基本訪問功能(通過get()函數)外,還有兩個常用的功能函數:expired()用於檢測所管理的對象是否已經釋放;lock()用於獲取所管理的對象的強引用指針。
通過boost::weak_ptr來打破循環引用
由於弱引用不更改引用計數,類似普通指針,只要把循環引用的一方使用弱引用,即可解除循環引用。對於上面的那個例子來說,只要把children的定義改爲如下方式,即可解除循環引用:
- class children
- {
- public:
- ~children() { std::cout <<"destroying children\n"; }
- public:
- boost::weak_ptr<parent> parent;
- };
- class children
- {
- public:
- ~children() { std::cout <<"destroying children\n"; }
- public:
- boost::weak_ptr<parent> parent;
- };
最後值得一提的是,雖然通過弱引用指針可以有效的解除循環引用,但這種方式必須在程序員能預見會出現循環引用的情況下才能使用,也可以是說這個僅僅是一種編譯期的解決方案,如果程序在運行過程中出現了循環引用,還是會造成內存泄漏的。因此,不要認爲只要使用了智能指針便能杜絕內存泄漏。畢竟,對於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.