5、?
例子:在容器中使用
shared_ptr
許多容器類,包括
STL
,都需要拷貝操作(例如,我們插入一個存在的元素到
list,vector,
或者
container
。)當拷貝操作是非常銷毀資源的時候(這些操作時必須的),典型的操作就是使用容器指針。
std::vector<CMyLargeClass *> vec;
vec.push_back( new CMyLargeClass("bigString") );
|
將內存管理的任務拋給調用者,我們能夠使用
shared_ptr
來實現。
typedef boost::shared_ptr<CMyLargeClass>??CMyLargeClassPtr;
std::vector<CMyLargeClassPtr> vec;
vec.push_back( CMyLargeClassPtr(new CMyLargeClass("bigString")) );
|
當
vector
被銷毀的時候,這個元素自動被銷毀了。當然,除非有另一個智能指針引用了它,則還本能被銷毀。讓我們看
Sample3
中的使用:
void
?Sample3_Container()
{
??
typedef
?boost::shared_ptr<CSample> CSamplePtr;
??// (A) create a container of CSample pointers:
??std::vector<CSamplePtr> vec;
??// (B) add three elements
??vec.push_back(CSamplePtr(
new
?CSample));
??vec.push_back(CSamplePtr(
new
?CSample));
??vec.push_back(CSamplePtr(
new
?CSample));
??// (C) "keep" a pointer to the second:
??CSamplePtr anElement = vec[1];
??// (D) destroy the vector:
??vec.clear();
??// (E) the second element still exists
??anElement->Use();
??printf("done. cleanup is automatic\n");
??// (F) anElement goes out of scope, deleting the last CSample instance
}
|
6、?
使用
Boost
中的智能指針,什么是正確的使用方法
使用智能指針的一些操作會產生錯誤(突出的事那些不可用的引用計數器,一些對象太容易釋放,或者根本釋放不掉)。
Boost
增強了這種安全性,處理了所有潛在存在的危險,所以我們要遵循以下幾條規(guī)則使我們的代碼更加安全。
下面幾條規(guī)則是你應該必須遵守的:
規(guī)則一:賦值和保存?
——
?
對于智能指針來說,賦值是立即創(chuàng)建一個實例,并且保存在那里?,F在智能指針擁有一個對象,你不能手動釋放它,或者取走它,這將幫助你避免意外地釋放了一個對象,但你還在引用它,或者結束一個不可用的引用計數器。
規(guī)則二:_ptr<T> 不是T*
?
——
?
恰當地說,不能盲目地將一個
T*?
和一個智能指針類型
T
相互轉換。意思是:
·?????????
當創(chuàng)建一個智能指針的時候需要明確寫出
?
__ptr<T> myPtr<new T>
。
·?????????
不能將
T*
賦值給一個智能指針。
·?????????
不能寫
ptr = NULL
,應該使用
ptr.reset()
。
·?????????
重新找回原始指針,使用
ptr.get()
,不必釋放這個指針,智能指針會去釋放、重置、賦值。使用
get()
僅僅通過函數指針來獲取原始指針。
·?????????
不能通過
T*
指向函數指針來代表一個
__ptr<T>
,需要明確構造一個智能指針,或者說將一個原始指針的所有權給一個指針指針。(見規(guī)則三)
·?????????
這是一種特殊的方法來認定這個智能指針擁有的原始指針。不過在
Boost:smart pointer programming techniques
?
舉例說明了許多通用的情況。
規(guī)則三:非循環(huán)引用
?
——
?
如果有兩個對象引用,而他們彼此都通過一個一個引用指針計數器,那么它們不能釋放,
Boost?
提供了
weak_ptr
來打破這種循環(huán)引用(下面介紹)。
規(guī)則四:非臨時的
?
share_ptr?
——
?
不能夠造一個臨時的
share_ptr
來指向它們的函數,應該命名一個局部變量來實現。(這可以使處理以外更安全,
Boost share_ptr best practices
?
有詳細解說)。
7、?
循環(huán)引用
引用計數器是一種便利的資源管理機制,它有一個基本回收機制。但循環(huán)引用不能夠自動回收,計算機很難檢測到。一個最簡單的例子,如下:
struct
?CDad;
struct
?CChild;
typedef
?boost::shared_ptr<CDad>???CDadPtr;
typedef
?boost::shared_ptr<CChild>??CChildPtr;
struct
?CDad : public CSample
{
???CChildPtr myBoy;
};
struct
?CChild : public CSample
{
?CDadPtr myDad;
};
// a "thing" that holds a smart pointer to another "thing":
CDadPtr???parent(
new
?CDadPtr);
CChildPtr child(
new
?CChildPtr);
// deliberately create a circular reference:
parent->myBoy = child;
child->myDad = dad;
// resetting one ptr...
child.reset();
|
?????????
parent
?
仍然引用
CDad
對象,它自己本身又引用
CChild
。整個情況如下圖所示:
如果我們調用
dad.reset()
,那么我們兩個對象都會失去聯系。但這種正確的離開這個引用,共享的指針看上去沒有理由去釋放那兩個對象,我們不能夠再訪問那兩個對象,但那兩個對象的確還存在,這是一種非常嚴重的內存泄露。如果擁有更多的這種對象,那么將由更多的臨界資源不能正常釋放。
???????
如果不能解決好共享智能指針的這種操作,這將是一個嚴重的問題(至少是我們不可接受的)。因此我們需要打破這種循環(huán)引用,下面有三種方法:
A、???
當只剩下最后一個引用的時候需要手動打破循環(huán)引用釋放對象。
B、???
當
Dad
的生存期超過
Child
的生存期的時候,
Child
需要一個普通指針指向
Dad
。
C、??
使用
boost::weak_ptr
打破這種循環(huán)引用。
方法
A
和
B
并不是一個完美的解決方案,但是可以在不使用
weak_ptr
的情況下讓我們使用智能指針,讓我們看看
weak_ptr
的詳細情況。
8、
?
使用
weak_ptr
跳出循環(huán)
強引用和弱引用的比較:
一個強引用當被引用的對象活著的話,這個引用也存在(就是說,當至少有一個強引用,那么這個對象就不能被釋放)。
boost::share_ptr
就是強引用。相對而言,弱引用當引用的對象活著的時候不一定存在。僅僅是當它存在的時候的一個引用。
boost::weak_ptr<T>?
是執(zhí)行弱引用的智能指針。當你需要它的時候就可以使用一個強(共享)指針指向它(當對象被釋放的時候,它為空),當然這個強指針在使用完畢應該立即釋放掉,在上面的例子中我們能夠修改它為弱指針。
struct CBetterChild : public CSample
{
??weak_ptr<CDad> myDad;
??void BringBeer()
??{
????shared_ptr<CDad> strongDad = myDad.lock(); // request a strong pointer
????if (strongDad)??????????????????????// is the object still alive?
??????strongDad->SetBeer();
??
??// strongDad is released when it goes out of scope.
????// the object retains the weak pointer
??}
};
|
9、
?
Intrusive_ptr
——輕量級共享智能指針
shared_ptr
比普通指針提供了更完善的功能。有一個小小的代價,那就是一個共享指針比普通指針占用更多的空間,每一個對象都有一個共享指針,這個指針有引用計數器以便于釋放。但對于大多數實際情況,這些都是可以忽略不計的。
intrusive_ptr
?
提供了一個折中的解決方案。它提供了一個輕量級的引用計數器,但必須對象本身已經有了一個對象引用計數器。這并不是壞的想法,當你自己的設計的類中實現智能指針相同的工作,那么一定已經定義了一個引用計數器,這樣只需要更少的內存,而且可以提高執(zhí)行性能。
如果你要使用
intrusive_ptr?
指向類型
T
,那么你就需要定義兩個函數:
intrusive_ptr_add_ref?
和
intrusive_ptr_release
。下面是一個簡單的例子解釋如何在自己的類中實現:
#include "boost/intrusive_ptr.hpp"
// forward declarations
class CRefCounted;
namespace boost
{
????void intrusive_ptr_add_ref(CRefCounted * p);
????void intrusive_ptr_release(CRefCounted * p);
};
// My Class
class CRefCounted
{
??private:
????long????references;
????friend void ::boost::intrusive_ptr_add_ref(CRefCounted * p);
????friend void ::boost::intrusive_ptr_release(CRefCounted * p);
??public:
????CRefCounted() : references(0) {}???// initialize references to 0
};
// class specific addref/release implementation
// the two function overloads must be in the boost namespace on most compilers:
namespace boost
{
?inline void intrusive_ptr_add_ref(CRefCounted * p)
??{
????// increment reference count of object *p
????++(p->references);
??}
?inline void intrusive_ptr_release(CRefCounted * p)
??{
???// decrement reference count, and delete object when reference count reaches 0
???if (--(p->references) == 0)
?????delete p;
??}
} // namespace boost
|
????????
?????????
這是一個最簡單的(非線程安全)實現操作。但作為一種通用的操作,如果提供一種基類來完成這種操作或許很有使用價值,也許在其他地方會介紹到。
10、
?
scoped_array?
和
?shared_array
scoped_array
?
和
?
shared_array
和上面講的
基本上相同,只不過
他們
是指向數組的。就像使用指針操作一樣使用
operator new[]?
,他們都重載了
operator new[]
。注意他們并不初始化分配長度。
11、
?
Boost
的安裝
從
www.boost.org
上下載最新版本的
boost
,然后解壓縮到你指定的目錄里,解壓縮后的文件目錄如下:
Boost\?????boost
的源文件和頭文件。
Doc\????????HTML
格式的文檔。
Lib\?????????
庫文件(不是必需的)
…?????????????
其他文件(“
more\
”里有其他資料)
添加目錄到我們自己的
IDE
里:
VC6
:在菜單
Tools/Options
,
Directories tab, "Show Directories for... Include files",
VC7
:
?
在菜單
Tools/Options,??Projects/VC++ directories, "Show Directories for... Include files".
Boost
的頭文件都在
boost\
子目錄里,例如本文檔例子中有
#include "boost/smart_ptr.hpp"
。所以任何人當讀到年的源文件的時候就立刻知道你用到了
boost
中的智能指針。
12、
?
關于本文檔中的例子
本文檔中的例子里有一個子目錄
boost\
僅僅包含了本例子中使用到的一些頭文件,僅僅是為了你編譯這個例子,如果你需要下載完整的
boost
或者獲取更多的資源請到
www.boost.org
。
13、
?
VC6
中
min/max
的災難
當在
VC
中使用
boost
庫,或者其他庫的時候會有一些小的問題。
在
Windows
的頭文件中已經定義了
min?
和
?max
宏,所以在
STL
中的這兩個函數就調用不到了,例如在
MFC
中就是這樣,但是在
Boost
中,都是使用的
std::
命名空間下的函數,使用
Windows
的函數不能夠接受不同類型的參數在模板中使用,但是許多庫都依賴這些。
雖然
Boost
盡量處理這些問題,但有時候遇到這樣的問題的時候就需要在自己的代碼中加入像下面的代碼在第一個
#include
前加入
#define _NOMINMAX
。
#define _NOMINMAX????????????// disable windows.h defining min and max as macros
#include "boost/config.hpp"??// include boosts compiler-specific "fixes"
using std::min;??????????????// makle them globally available
using std::max;
|
?????????
這樣操作并不是在任何時候都需要,而只有我們碰到使用了就需要加入這段代碼。
14、
?
資源
獲取更多的信息,或者有問題可以查找如下資源:
·?????????
Boost home page
·?????????
Download Boost
·?????????
Smart pointer overview
·?????????
Boost users mailing list
·?????????
Boost中的智能指針
(
撰文??Bjorn Karlsson????翻譯??曾毅)