1.智能指針類型
C++98最早的智能指針auto_ptr已被廢止。
C++11/14標准中的unique_ptr、shared_ptr和weak_ptr,源於boost中的scoped_ptr、shared_ptr和weak_ptr(boost中共有6種智能指針)
2.scoped_ptr(scoped_array)和unique_ptr
2.1 scoped_ptr(scoped_array)
scoped_ptr包裝了new在堆上的動態對象,能保證對象能在任何時候都被正確的刪除。scoped_ptr的所有權更嚴格,不能轉讓。scoped_ptr,看名字,該智能指針只可以在本作用域內使用,不希望被轉讓。scoped_array和scoped_ptr的唯一不同是scoped_array管理數組對象。不建議使用scoped_array,可用vector替代。
scoped_ptr摘要:
template<class T> class scoped_ptr // noncopyable
{
private:
T * px;//原始指針
//禁止對智能指針的拷貝,保證被它管理的指針不被轉讓所有權。
//如果一個類持有scoped_ptr成員,它也是禁止拷貝賦值的。
scoped_ptr(scoped_ptr const &);//拷貝構造私有化
scoped_ptr & operator=(scoped_ptr const &);//賦值操作私有化
typedef scoped_ptr<T> this_type;
void operator==( scoped_ptr const& ) const;
void operator!=( scoped_ptr const& ) const;
public:
typedef T element_type;
explicit scoped_ptr( T * p = 0 ); // never throws
explicit scoped_ptr( std::auto_ptr<T> p );
~scoped_ptr(); // never throws
void reset(T * p = 0); // never throws,重置指針,一般不用
//模擬原始指針操作,保存空指針時2操作未定義
T & operator*() const; // never throws
T * operator->() const; // never throws
T * get() const;//獲得原始指針
void swap(scoped_ptr & b);//交換指針
};
2.2 unique_ptr
std::unique_ptr是C++11中新定義的智能指針,用於取代auto_ptr。unique_ptr不僅可以代理new創建的單個對象,也可以代理new[]創建的數組對象,就是說它結合了scoped_ptr和scoped_array兩者的能力。
unique_ptr的基本能力跟scoped_ptr一樣,同樣可以在作用域內管理指針,也不允許拷貝和賦值。
unique_ptr類摘要:
template <class T, class D = default_delete<T> >
class unique_ptr
{
public:
unique_ptr(const unique_ptr&) = delete;//禁止拷貝
unique_ptr& operator=(const unique_ptr&) = delete;//禁止賦值
public:
BOOST_CONSTEXPR unique_ptr();
BOOST_CONSTEXPR unique_ptr(BOOST_MOVE_DOC0PTR(bmupd::nullptr_type)) BOOST_NOEXCEPT;
unique_ptr(BOOST_RV_REF(unique_ptr) u) BOOST_NOEXCEPT;
unique_ptr& operator=(BOOST_RV_REF(unique_ptr) u) BOOST_NOEXCEPT;
operator=(BOOST_RV_REF_BEG unique_ptr<U, E> BOOST_RV_REF_END u) BOOST_NOEXCEPT;//轉移語義賦值
pointer operator->() const BOOST_NOEXCEPT;
pointer get() const BOOST_NOEXCEPT;
pointer release() BOOST_NOEXCEPT;//釋放指針管理權
reset(Pointer p) BOOST_NOEXCEPT;
void swap(unique_ptr& u) BOOST_NOEXCEPT;
3 shared_ptr和weak_ptr
3.1 shared_ptr
shared_ptr是最像指針的智能指針。shared_ptr和scoped_ptr一樣包裝了new在堆上的動態對象,也不可以管理new[]產生的數組指針,也沒有指針算術操作。但是shared_ptr實現了引用計數,可以自由拷貝賦值,在任意地方共享它,當沒有代碼使用它(引用計數為0)時,它才刪除被包裝的動態對象。shared_ptr可以被放在標准容器中,STL容器存儲指針的標准做法。
3.1.1 工廠函數make_shared
shared_ptr消除了顯示調用delete,但是沒有消除顯示調用new,boost提供一個工廠函數make_shared來消除顯式new調用。例子:
void fun()
{
auto sp = boost::make_shared<string>("make_shared");//創建string共享指針
auto spv = boost::make_shared< vector<int> >(10,2);//vector共享指針
}
3.2 weak_ptr
weak_ptr是為了配合shared_ptr而引入的,它沒有普通指針的行為,沒重載*和->。它最大的作用是協助shared_ptr工作,像旁觀者一樣觀測資源的使用情況。weak_ptrk可以從一個shared_ptr或者一個weak_ptr對象構造,獲得對象資源觀測權。但是weak_ptr並沒有共享資源,它的構造不會引起引用計數的增加,也不會讓引用計數減少,它只是靜靜的觀察者。
template<class T> class weak_ptr
{
private:
typedef weak_ptr<T> this_type;
public:
weak_ptr();
~weak_ptr();
weak_ptr( weak_ptr const & r );
weak_ptr & operator=( weak_ptr const & r );
weak_ptr( weak_ptr<Y> const & r );
shared_ptr<T> lock() const;//獲取shared_ptr,把弱關系轉為強關系,操作資源
long use_count() const;//引用計數
bool expired() const;//是否失效指針
void reset();//重置指針
void swap(this_type & other);
element_type * px; // contained pointer
boost::detail::weak_count pn; // reference counter
}; // weak_ptr
template<class T, class U> inline bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b);
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b);
例子:
class self_shared: public enable_shared_from_this<self_shared>
{
public:
int n;
void fun();
};
int main()
{
auto sp = make_shared<self_shared>(123);
sp->fun();
auto p = sp->shared_from_this();
p->fun();
//不能對普通對象使用shared_from_this
self_shared s;
auto ps = s->shared_from_this();//錯誤,shared_ptr會在析構時試圖刪除一個棧對象
return 0;
}
3.3 打破循環引用
有時候代碼會出現循環引用,這時候shared_ptr的引用計數就會失效,導致不能正確釋放資源。例如:
class node
{
public:
~node(){cout<<"deleted.\n";}
typedef shared_ptr<node> ptr_type;
ptr_type next;
};
int main()
{
auto p1 = make_shared<node>();
auto p2 = make_shared<node>();
p1->next = p2;//形成循環鏈表
p2->next = p1;
cout<<p1->use_count()<<endl;//=2
cout<<p2->use_count()<<endl;//=2
return 0;
}
兩個node對象都互相持有對方的引用,每個shared_ptr對象的引用計數都是2,因此在析構時不能減為0,不會調用刪除操作,導致內存泄漏。這個時候我們可以使用weak_ptr,因為它不會增加引用計數,這強引用變為弱引用,在可能循環的地方打破循環,真正需要shared_ptr的時候調用weak_ptr的lock()函數。
//修改以上
typedef weak_ptr<node> ptr_type;
p1->next = p2;//形成循環鏈表
p2->next = p1;//weak_ptr所以正常
if (!p1->next->expired())//檢查弱引用是否有效
{
auto p3 = p1->next->lock();//獲得強引用
}
//退出后,shared_ptr都正確析構
