shared_ptr
shared_ptr
shared_ptr是一種智能指針(smart pointer),作用有如同指針,但會記錄有多少個shared_ptrs共同指向一個對象。這便是所謂的引用計數(reference counting)。
一旦最後一個這樣的指針被銷毀,也就是一旦某個對象的引用計數變為0,這個對象會被自動刪除。這在非環形數據結構中防止資源泄露很有幫助。
auto_ptr由於它的破壞性複製語義,無法滿足標準容器對元素的要求,因而不能放在標準容器中;如果我們希望當容器析構時能自動把它容納的指針元素所指的對象刪除時,通常採用一些間接的方式來實現,顯得比較繁瑣。boost庫中提供了一種新型的智能指針shared_ptr,它解決了在多個指針間共享對象所有權的問題,同時也滿足容器對元素的要求,因而可以安全地放入容器中。
shared_ptr最初實現於Boost庫中,後來被C++標準委員會收錄於TR1技術報告中,成為C++11標準的一部分。
namespace boost {
class bad_weak_ptr: public std::exception;
template class weak_ptr;
template class shared_ptr {
public:
typedef T element_type;
shared_ptr(); // never throws
template explicit shared_ptr(Y * p);
template shared_ptr(Y * p, D d);
template shared_ptr(Y * p, D d, A a);
~shared_ptr(); // never throws
shared_ptr( shared_ptr(shared_ptr const & r); // never throws
template shared_ptr(shared_ptr const & r, T * p); // never throws
template explicit shared_ptr(weak_ptr const & r);
template explicit shared_ptr(std::auto_ptr & r);
shared_ptr & operator=( shared_ptr & operator=(shared_ptr const & r); // never throws
template shared_ptr & operator=(std::auto_ptr & r);
void reset(); // never throws
template void reset(Y * p);
template void reset(Y * p, D d);
template void reset(Y * p, D d, A a);
template void reset(shared_ptr const & r, T * p); // never throws
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
operatorunspecified-bool-type() const; // never throws
void swap(shared_ptr & b); // never throws
};
template
bool operator==(shared_ptr const & a, shared_ptr const & b); // never throws
template
bool operator!=(shared_ptr const & a, shared_ptr const & b); // never throws
template
bool operator<(shared_ptr const & a, shared_ptr const & b); // never throws
template void swap(shared_ptr & a, shared_ptr & b); // never throws
template T * get_pointer(shared_ptr const & p); // never throws
template
shared_ptr static_pointer_cast(shared_ptr const & r); // never throws
template
shared_ptr const_pointer_cast(shared_ptr const & r); // never throws
template
shared_ptr dynamic_pointer_cast(shared_ptr const & r); // never throws
template
std::basic_ostream & operator<<(std::basic_ostream & os, shared_ptr const & p);
template
D * get_deleter(shared_ptr const & p);
}
使用shared_ptr解決的主要問題是知道刪除一個被多個客戶共享的資源的正確時機。下面是一個簡單易懂的例子,有兩個類 A和 B, 它們共享一個int實例。使用 boost::shared_ptr, 你必須包含"boost/shared_ptr.hpp".
類 A和 B都保存了一個 shared_ptr. 在創建 A和 B的實例時,shared_ptr temp被傳送到它們的構造函數。這意味著共有三個 shared_ptr:a, b, 和 temp,它們都引向同一個int實例。如果我們用指針來實現對一個的共享,A和 B必須能夠在某個時間指出這個int要被刪除。在這個例子中,直到main的結束,引用計數為3,當所有 shared_ptr離開了作用域,計數將達到0,而最後一個智能指針將負責刪除共享的 int.
把對象直接存入容器中有時會有些麻煩。以值的方式保存對象意味著使用者將獲得容器中的元素的拷貝,對於那些複製是一種昂貴的操作的類型來說可能會有性能的問題。此外,有些容器,特別是 std::vector, 當你加入元素時可能會複製所有元素,這更加重了性能的問題。最後,傳值的語義意味著沒有多態的行為。如果你需要在容器中存放多態的對象而且你不想切割它們,你必須用指針。如果你用裸指針,維護元素的完整性會非常複雜。從容器中刪除元素時,你必須知道容器的使用者是否還在引用那些要刪除的元素,不用擔心多個使用者使用同一個元素。這些問題都可以用shared_ptr來解決。
下面是如何把共享指針存入標準庫容器的例子。
這裡有兩個類, A和 B, 各有一個虛擬成員函數 sing. B從 A公有繼承而來,並且如你所見,工廠函數 createA返回一個動態分配的B的實例,包裝在shared_ptr里。在 main里, 一個包含shared_ptr的 std::vector被放入10個元素,最後對每個元素調用sing。如果我們用裸指針作為元素,那些對象需要被手工刪除。而在這個例子里,刪除是自動的,因為在vector的生存期中,每個shared_ptr的引用計數都保持為1;當 vector被銷毀,所有引用計數器都將變為零,所有對象都被刪除。有趣的是,即使 A的析構函數沒有聲明為 virtual, shared_ptr也會正確調用 B的析構函數!
上面的例子示範了一個強有力的技術,它涉及A裡面的protected析構函數。因為函數 createA返回的是 shared_ptr, 因此不可能對shared_ptr::get返回的指針調用 delete。這意味著如果為了向某個需要裸指針的函數傳送裸指針而從shared_ptr中取出裸指針的話,它不會由於意外地被刪除而導致災難。那麼,又是如何允許 shared_ptr刪除它的對象的呢?這是因為指針指向的真正類型是 B; 而B的析構函數不是protected的。這是非常有用的方法,用於給shared_ptr中的對象增加額外的安全性。