?
快捷搜索:  as  test  1111  test aNd 8=8  test++aNd+8=8  as++aNd+8=8  as aNd 8=8

新蒲京澳門賭場網站:C++中的健壯指針和資源管理

?

我最愛好的對資本的定義是:"任何在你的法度榜樣中得到并在此后開釋的器械。"內存是一個相稱顯著的資本的例子。它必要用new來得到,用delete來開釋。同時也有許多其它類型的資本文件句柄、緊張的片斷、Windows中的GDI資本,等等。將資本的觀點推廣到法度榜樣中創建、開釋的所有工具也是十分方便的,無論工具是在堆平分配的照樣在棧中或者是在全局感化于內生命的。

我最愛好的對資本的定義是:"任何在你的法度榜樣中得到并在此后開釋的器械。"內存是一個相稱顯著的資本的例子。它必要用new來得到,用delete來開釋。同時也有許多其它類型的資本文件句柄、緊張的片斷、Windows中的GDI資本,等等。將資本的觀點推廣到法度榜樣中創建、開釋的所有工具也是十分方便的,無論工具是在堆平分配的照樣在棧中或者是在全局感化于內生命的。

資本及它們的所有權

我最愛好的對資本的定義是:"任何在你的法度榜樣中得到并在此后開釋的器械?quot;內存是一個相稱顯著的資本的例子。它必要用new來得到,用delete來開釋。同時也有許多其它類型的資本文件句柄、緊張的片斷、Windows中的GDI資本,等等。將資本的觀點推廣到法度榜樣中創建、開釋的所有工具也是十分方便的,無論工具是在堆平分配的照樣在棧中或者是在全局感化于內生命的。

對付給定的資本的擁有著,是認真開釋資本的一個工具或者是一段代碼。所有權分立為兩種級別--自動的和顯式的(automatic and explicit),假如一個工具的開釋是由說話本身的機制來包管的,這個工具的便是被自動地所有。例如,一個嵌入在其他工具中的工具,他的清除必要其他工具來在清除的時刻包管。外貌的工具被看作嵌入類的所有者。   類似地,每個在棧上創建的工具(作為自動變量)的開釋(破壞)是在節制流落開了工具被定義的感化域的時刻包管的。這種環境下,感化于被看作是工具的所有者。留意所有的自動所有權都是和說話的其他機制相容的,包括非常。無論是若何退出感化域的--正常流程節制退出、一個break語句、一個return、一個goto、或者是一個throw--自動資本都可以被清除。

到今朝為止,統統都很好!問題是在引入指針、句柄和抽象的時刻孕育發生的。假如經由過程一個指針造訪一個工具的話,比如工具在堆平分配,C++不自動地關注它的開釋。法度榜樣員必須明確的用適當的法度榜樣措施來開釋這些資本。比如說,假如一個工具是經由過程調用new來創建的,它必要用delete來收受接收。一個文件是用CreateFile(Win32 API)打開的,它必要用CloseHandle來關閉。用EnterCritialSection進入的臨界區(Critical Section)必要LeaveCriticalSection退出,等等。一個"裸"指針,文件句柄,或者臨界區狀態沒有所有者來確保它們的終極開釋?;A的資本治理的條件便是確保每個資本都有他們的所有者。

第一規則

一個指針,一個句柄,一個臨界區狀態只有在我們將它們封裝入工具的時刻才會擁有所有者。這便是我們的第一規則:在構造函數平分配資本,在析構函數中開釋資本。

當你按照規則將所有資本封裝的時刻,你可以包管你的法度榜樣中沒有任何的資本泄露。這點在當封裝工具(Encapsulating Object)在棧中建立或者嵌入在其他的工具中的時刻異常顯著。然則對那些動態申請的工具呢?不要急!任何動態申請的器械都被看作一種資本,并且要按照上面提到的措施進行封裝。這一工具封裝工具的鏈不得不在某個地方終止。它終極終止在最高檔的所有者,自動的或者是靜態的。這些分手是對脫離感化域或者法度榜樣時開釋資本的包管。

下面是資本封裝的一個經典例子。在一個多線程的利用法度榜樣中,線程之間共享工具的問題是經由過程用這樣一個工具聯系臨界區來辦理的。每一個必要造訪共享資本的客戶必要得到臨界區。例如,這可能是Win32下臨界區的實現措施。

class CritSect

{

friend class Lock;

public:

CritSect () { InitializeCriticalSection (&_critSection); }

~CritSect () { DeleteCriticalSection (&_critSection); }

private

void Acquire ()

{

EnterCriticalSection (&_critSection);

}

void Release ()

{

LeaveCriticalSection (&_critSection);

}

CRITICAL_SECTION _critSection;

};

這里智慧的部分是我們確保每一個進入臨界區的客戶著末都可以脫離。"進入"臨界區的狀態是一種資本,并該當被封裝。封裝器平日被稱作一個鎖(lock)。

class Lock

{

public:

Lock (CritSect& critSect) : _critSect (critSect)

{

_critSect.Acquire ();

}

~Lock ()

{

_critSect.Release ();

}

private

CritSect & _critSect;

};

鎖一樣平常的用法如下:

void Shared::Act () throw (char *)

{

Lock lock (_critSect);

// perform action -- may throw

// automatic destructor of lock

}

留意無論發生什么,臨界區都邑借助于說話的機制包管開釋。

還有一件必要記著的工作--每一種資本都必要被分手封裝。這是由于資本分配是一個異常輕易掉足的操作,是要資本是有限供給的。我們會假設一個掉敗的資本分配會導致一個非常--事實上,這會常常的發生。以是假如你想試圖用一個石頭打兩只鳥的話,或者在一個構造函數中申請兩種形式的資本,你可能就會陷入麻煩。只要想想在一種資本分配成功但另一種掉敗拋出非常時會發生什么。由于構造函數還沒有整個完成,析構函數弗成能被調用,第一種資本就會發生泄露。

這種環境可以異常簡單的避免。無論何時你有一個必要兩種以上資本的類時,寫兩個笑的封裝器將它們嵌入你的類中。每一個嵌入的構造都可以包管刪除,縱然包裝類沒有構造完成。

Smart Pointers

我們至今還沒有評論爭論最常見類型的資本--用操作符new分配,此后用指針造訪的一個工具。我們必要為每個工具分手定義一個封裝類嗎?(事實上,C++標準模板庫已經有了一個模板類,叫做auto_ptr,其感化便是供給這種封裝。我們一下子在回到auto_ptr。)讓我們從一個極其簡單、枯燥但安然的器械開始??聪旅娴腟mart Pointer模板類,它十分穩固,以致無法實現。

template

class SPtr

{

public:

~SPtr () { delete _p; }

T * operator->() { return _p; }

T const * operator->() const { return _p; }

pro新蒲京澳門賭場網站tected:

SPtr (): _p (0) {}

explicit SPtr (T* p): _p (p) {}

T * _p;

};

為什么要把SPtr的構造函數設計為protected呢?假如我必要遵守第一條規則,那么我就必須這樣做。資本--在這里是class T的一個工具--必須在封裝器的構造函數平分配。然則我不能只簡單的調用new T,由于我不知道T的構造函數的參數。由于,在原則上,每一個T都有一個不合的構造函數;我必要為他定義個別的一個封裝器。模板的用場會很大年夜,為每一個新的類,我可以經由過程承襲SPtr定義一個新的封裝器,并且供給一個特定的構造函數。

class SItem: public SPt新蒲京澳門賭場網站r

{

public:

explicit SItem (int i)

: SPtr (new Item (i)) {}

};

為每一個類供給一個Smart Pointer真的值得嗎?說實話--不!他很有教授教化的代價,然則一旦你學會若何遵照第一規則的話,你就可以放松規則并應用一些高檔的技巧。這一技巧是讓SPtr的構造函數成為public,然則只是是用它來做資本轉換(Resource Transfer)我的意思是用new操作符的結果直接作為SPtr的構造函數的參數,像這樣:

SPtr item (new Item (i));

這個措施顯著更必要自控性,不光是你,而且包括你的法度榜樣小組的每個成員。他們都必須賭咒出了作資本轉換外不把構造函數用在人以其他用途。幸運的是,這條規矩很輕易得以加強。只必要在源文件中查找所有的new即可。

Resource Transfer

到今朝為止,我們所評論爭論的不停是生命周期在一個零丁的感化域內的資本?,F在我們要辦理一個艱苦的問題--若何在不合的感化域間安然的通報資本。這一問題在當你處置懲罰容器的時刻會變得十分顯著。你可以動態的創建一串工具,將它們寄放至一個容器中,然后將它們掏出,并且在終極安排它們。為了能夠讓這安然的事情--沒有泄露--工具必要改變其所有者。

這個問題的一個異常顯而易見的辦理措施是應用Smart Pointer,無論是在加入容器前照樣還找到它們今后。這是他若何運作的,你加入Release措施到Smart Pointer中:

template

T * SPtr::Release ()

{

T * pTmp = _p;

_p = 0;

return pTmp;

}

留意在Release調用今后,Smart Pointer就不再是工具的所有者了--它內部的指針指向空?,F在,調用了Release都必須是一個認真的人并且迅速暗藏返回的指針到新的所有者工具中。在我們的例子中,容器調用了Release,比如這個Stack的例子:

void Stack::Push (SPtr& item) throw (char *)

{

if (_top == maxStack)

throw "Stack overflow";

_arr [_top++] = item.Release ();

};

同樣的,你也可以再你的代碼頂用加強Release的靠得住性。

響應的Pop措施要做些什么呢?他應該開釋了資本并祈禱調用它的是一個認真的人而且急速作一個資本通報它到一個Smart Pointer?這聽起來并不好。

Strong Pointers

資本治理在內容索引(Windows NT Server上的一部分,現在是Windows 2000)上事情,并且,我對這十分知足。然后我開始想……這一措施是在這樣一個完備的系統中形成的,假如可以把它內建入說話的本身豈不是一件異常好?我提出了強指針(Strong Pointer)和弱指針(Weak Pointer)。一個Strong Pointer會在許多地方和我們這個SPtr相似--它在越過它的感化域后會清除他所指向的工具。資本通報會以強指針賦值的形式進行。也可以有Weak Pointer存在,它們用來造訪工具而不必要所有工具--比如可賦值的引用。

任何指針都必須聲明為Strong或者Weak,并且說話應該來關注類型轉換的規定。例如,你弗成以將Weak Pointer通報到一個必要Strong Pointer的地方,然則相反卻可以。Push措施可以吸收一個Strong Pointer并且將它轉移到Stack中的Strong Pointer的序列中。Pop措施將會返回一個Strong Pointer。把Strong Pointer的引入說話將會使垃圾收受接收獲為歷史。

這里還有一個小問題--改動C++標準險些和競選美國總統一樣輕易。當我將我的留意奉告給Bjarne Stroutrup的時刻,他看我的眼神似乎是我剛剛要向他借一千美元一樣。

然后我忽然想到一個動機。我可以自己實現Strong Pointers。終究,它們都很想Smart Pointers。給它們一個拷貝構造函數并重載賦值操作符并不是一個大年夜問題。事實上,這恰是標準庫中的auto_ptr有的。緊張的是對這些操作給出一個資本轉移的語法,然則這也不是很難。

template

SPtr::SPtr (SPtr & ptr)

{

_p = ptr.Release ();

}

template

void SPtr::operator = (SPtr & ptr)

{

if (_p != ptr._p)

{

delete _p;

_p = ptr.Release ();

}

}

使這全部設法主見迅速成功的緣故原由之一是我可以以值要領通報這種封裝指針!我有了我的蛋糕,并且也可以吃了??催@個Stack的新的實現:

class Stack

{

enum { maxStack = 3 };

public:

Stack ()

: _top (0)

{}

void Push (SPtr & item) throw (char *)

{

if (_top >= maxStack)

throw "Stack overflow";

_arr [_top++] = item;

}

SPtr Pop ()

{

if (_top == 0)

return SPtr ();

return _arr [--_top];

}

private

int _top;

SPtr _arr [maxStack];

};

Pop措施強制客戶將其返回值賦給一個Strong Pointer,SPtr。任何試圖將他對一個通俗指針的賦值都邑孕育發生一個編譯期差錯,由于類型不匹配。此外,由于Pop以值要領返回一個Strong Pointer(在Pop的聲明時SPtr后面沒有&符號),編譯器在return時自動進行了一個資本轉換。他調用了operator =來從數組中提取一個Item,拷貝構造函數將他通報給調用者。調用者著末擁有了指向Pop賦值的Strong Pointer指向的一個Item。

我頓時意識到我已經在某些器械之上了。我開始用了新的措施重寫原本的代碼。

闡發器(Parser)

我以前有一個老的算術操作闡發器,是用老的資本治理的技巧寫的。闡發器的感化是在闡發樹中天生節點,節點是動態分配的。例如闡發器的Expression措施天生一個表達式節點。我沒有光陰用Strong Pointer去重寫這個闡發器。我令Expression、Term和Factor措施以傳值的要領將Strong Pointer返回到Node中??聪旅娴腅xpression措施的實現:

SPtr Parser::Expression()

{

// Parse a term

SPtr pNode = Term ();

EToken token = _scanner.Token();

if ( token == tPlus || token == tMinus )

{

// Expr := Term { ('+' | '-') Term }

SPtr pMultiNode = new SumNode (pNode);

do

{

_scanner.Accept();

SPtr pRight = Term ();

pMultiNode->AddChild (pRight, (token == tPlus));

token = _scanner.Token();

} while (token == tPlus || token == tMinus);

pNode = up_cast (pMultiNode);

}

// otherwise Expr := Term

return pNode; // by value!

}

最開始,Term措施被調用。他傳值返回一個指向Node的Strong Pointer并且立即把它保存到我們自己的Strong Pointer,pNode中。假如下一個符號不是加號或者減號,我們就簡單的把這個SPtr以值返回,這樣就開釋了Node的所有權。別的一方面,假如下一個符號是加號或者減號,我們創建一個新的SumMode并且立即(直接通報)將它儲存到MultiNode的一個Strong Pointer中。這里,SumNode是從MultiMode中承襲而來的,而MulitNode是從Node承襲而來的。原本的Node的所有權轉給了SumNode。

只如果他們在被加號和減號分開的時刻,我們就賡續的創建terms,我們將這些term轉移到我們的MultiNode中,同時MultiNode獲得了所有權。著末,我們將指向MultiNode的Strong Pointer向上映射為指向Mode的Strong Pointer,并且將他返回調用著。

我們必要對Strong Pointers進行顯式的向上映射,縱然指針是被隱式的封裝。例如,一個MultiNode是一個Node,然則相同的is-a關系在SPtr和SPtr之間并不存在,由于它們是分離的類(模板實例)并不存在承襲關系。up-cast模板是像下面這樣定義的:

template

inline SPtr up_cast (SPtr & from)

{

return SPtr (from.Release ());

}

假如你的編譯器支持新加入標準的成員模板(member template)的話,你可以為SPtr定義一個新的構造函數用來從吸收一個class U。

template

templateSPtr::SPtr (SPrt & uptr)

: _p (uptr.Release ())

{}

這里的這個花招是模板在U不是T的子類的時刻就不會編譯成功(換句話說,只在U is-a T的時刻才會編譯)。這是由于uptr的緣故。Release()措施返回一個指向U的指針,并被賦值為_p,一個指向T的指針。以是假如U不是一個T的話,賦值會導致一個編譯時候差錯。

std::auto_ptr

后來我意識到在STL中的auto_ptr模板,便是我的Strong Pointer。在那時刻還有許多的實現差異(auto_ptr的Release措施并不將內部的指針清零--你的編譯器的庫很可能用的便是這種迂腐的實現),然則著末在標準被廣泛吸收之前都被辦理了。

Transfer Semantics(轉換語義學)

今朝為止,我們不停在評論爭論在C++新蒲京澳門賭場網站法度榜樣中資本治理的措施。宗旨是將資本封裝到一些輕量級的類中,并由類認真它們的開釋。特其余是,所有用new操作符分配的資本都邑被儲存并通報進Strong Pointer(標準庫中的auto_ptr)的內部。

這里的關鍵詞是通報(passing)。一個容器可以經由過程傳值返回一個Strong Pointer來安然的開釋資本。容器的客戶只能夠經由過程供給一個響應的Strong Pointer來保存這個資本。任何一個將結果賦給一個"裸"指針的做法都急速會被編譯器發明。

auto_ptr item = stack.Pop (); // ok

Item * p = stack.Pop (); // Error! Type mismatch.

以傳值要領被通報的工具有value semantics 或者稱為 copy semantics。Strong Pointers因此值要領通報的--然則我們能說它們有copy semantics嗎?不是這樣的!它們所指向的工具肯定沒有被拷貝過。事實上,通報過后,源auto_ptr不在造訪原有的工具,并且目標auto_ptr成為了工具的獨一擁有者(然則每每auto_ptr的舊的實現縱然在開釋后仍舊維持著對工具的所有權)。自然而然的我們可以將這種新的行徑稱作Transfer Semantics。

拷貝構造函數(copy construcor)和賦值操作符定義了auto_ptr的Transfer Semantics,它們用了非const的auto_ptr引用作為它們的參數。

auto_ptr (auto_ptr & ptr);

auto_ptr & operator = (auto_ptr & ptr);

這是由于它們確鑿改變了他們的源--剝奪了對資本的所有權。

經由過程定義響應的拷貝構造函數和重載賦值操作符,你可以將Transfer Semantics加入到許多工具中。例如,許多Windows中的資本,比如動態建立的菜單或者位圖,可以用有Transfer Semantics的類來封裝。

Strong Vectors

標準庫只在auto_ptr中支持資本治理。以致連最簡單的容器也不支持ownership semantics。你可能想將auto_ptr和標準容器組合到一路可能會管用,然則并不是這樣的。例如,你可能會這樣做,然則會發明你不能夠用標準的措施來進行索引。

vector > autoVector;

這種建造不會編譯成功;

Item * item = autoVector [0];

另一方面,這會導致一個從autoVect到auto_ptr的所有權轉換:

auto_ptr item = autoVector [0];

我們沒有選擇,只能夠構造我們自己的Strong Vector。最小的接口應該如下:

template

class auto_vector

{

public:

explicit auto_vector (size_t capacity = 0);

T const * operator [] (size_t i) const;

T * operator [] (size_t i);

void assign (size_t i, auto_ptr & p);

void assign_direct (size_t i, T * p);

void push_back (auto_ptr & p);

auto_ptr pop_back ();

};

你大概會發明一個異常防御性的設計立場。我抉擇不供給一個對vector的左值索引的造訪,取而代之,假如你想設定(set)一個值的話,你必須用assign或者assign_direct措施。我的不雅點是,資本治理不應該被漠視,同時,也不應該在所有的新蒲京澳門賭場網站地方濫用。在我的履歷里,一個strong vector常常被許多push_back措施充斥著。

Strong vector最好用一個動態的Strong Pointers的數組來實現:

template

class auto_vector

{

private

void grow (size_t reqCapacity);

auto_ptr *_arr;

size_t _capacity;

size_t _end;

};

grow措施申請了一個很大年夜的auto_ptr的數組,將所有的器械從老的書組類轉移出來,在此中互換,并且刪除原本的數組。

auto_vector的其他實現都是十分直接的,由于所有資本治理的繁雜度都在auto_ptr中。例如,assign措施簡單的使用了重載的賦值操作符來刪除原有的工具并轉移資本到新的工具:

void assign (size_t i, auto_ptr & p)

{

_arr [i] = p;

}

我已經評論爭論了push_back和pop_back措施。push_back措施傳值返回一個auto_ptr,由于它將所有權從auto_vector轉換到auto_ptr中。

對auto_vector的索引造訪是借助auto_ptr的get措施來實現的,get簡單的返回一個內部指針。

T * operator [] (size_t i)

{

return _arr [i].get ();

}

沒有容器可以沒有iterator。我們必要一個iterator讓auto_vector看起來更像一個通俗的指針向量。分外是,當我們廢棄iterator的時刻,我們必要的是一個指針而不是auto_ptr。我們不盼望一個auto_vector的iterator在無意中進行資本轉換。

template

class auto_iterator: public

iterator

{

public:

auto_iterator () : _pp (0) {}

auto_iterator (auto_ptr * pp) : _pp (pp) {}

bool operator != (auto_iterator const & it) const

{ return it._pp != _pp; }

auto_iterator const & operator++ (int) { return _pp++; }

auto_iterator operator++ () { return ++_pp; }

T * operator * () { return _pp->get (); }

private

auto_ptr * _pp;

};

我們給auto_vect供給了標準的begin和end措施來找回iterator:

class auto_vector

{

public:

typedef auto_iterator iterator;

iterator begin () { return _arr; }

iterator end () { return _arr + _end; }

};

你大概會問我們是否要使用資本治理從新實現每一個標準的容器?幸運的是,不;事實是strong vector辦理了大年夜部分所有權的需求。當你把你的工具都安然的放置到一個strong vector中,你可以用所有其它的容器來從新安排(weak)pointer。

設想,例如,你必要對一些動態分配的工具排序的時刻。你將它們的指針保存到一個strong vector中。然后你用一個標準的vector來保存從strong vector中得到的weak指針。你可以用標準的算法對這個vector進行排序。這種中介vector叫做permutation vector。相似的,你也可以用標準的maps, priority queues, heaps, hash tables等等。

Code Inspection(編碼反?。?/p>

假如你嚴格遵循資本治理的條目,你就不會再資本泄露或者兩次刪除的地方碰到麻煩。你也低落了造訪野指針的幾率。同樣的,遵照原有的規則,用delete刪除用new申請的德指針,不要兩次刪除一個指針。你也不會碰到麻煩。然則,那個是更好的留意呢?

這兩個措施有一個很大年夜的不合點。便是和探求傳統措施的bug比擬,找到違反資本治理的規定要輕易的多。后者僅必要一個代碼檢測或者一個運行測試,而前者則在代碼中暗藏得很深,并必要很深的反省。

設想你要做一段傳統的代碼的內存泄露反省。第一件事,你要做的便是grep所有在代碼中呈現的new,你必要找出被分配空間地指針都作了什么。你必要確定導致刪除這個指針的所有的履行路徑。你必要反省break語句,歷程返回,非常。原有的指針可能賦給另一個指針,你對這個指針也要做相同的事。

比擬之下,對付一段用資本治理技巧實現的代碼。你也用grep反省所有的new,然則此次你只必要反省左新蒲京澳門賭場網站近的調用:

● 這是一個直接的Strong Pointer轉換,照樣我們在一個構造函數的函數體中?

● 調用的返回知是否急速保存到工具中,構造函數中是否有可以孕育發生非常的代碼。?

● 假如這樣的話析構函數中時刻有delete?

下一步,你必要用grep查找所有的release措施,并實施相同的反省。

不合點是必要反省、理解單個履行路徑和只必要做一些本地的查驗。這難道不是提醒你非布局化的和布局化的法度榜樣設計的不合嗎?道理上,你可以覺得你可以敷衍goto,并且跟蹤所有的可能分支。另一方面,你可以將你的狐疑本地化為一段代碼。本地化在兩種環境下都是關鍵所在。

在資本治理中的差錯模式也對照輕易調試。最常見的bug是試圖造訪一個開釋過的strong pointer。這將導致一個差錯,并且很輕易跟蹤。

共享的所有權

為每一個法度榜樣中的資本都找出或者指定一個所有者是一件很輕易的工作嗎?謎底是出乎料想的,是!假如你發清楚明了一些問題,這可能闡明你的設計上存在問題。還有另一種環境便是共享所有權是最好的以致是獨一的選擇。

共享的責任分配給被共享的工具和它的客戶(client)。一個共享資本必須為它的所有者維持一個引用計數。另一方面,所有者再開釋資本的時刻必須傳遞共享工具。著末一個開釋資本的必要在著末認真free的事情。

最簡單的共享的實現是共享工具承襲引用計數的類RefCounted:

class RefCounted

{

public:

RefCounted () : _count (1) {}

int GetRefCount () const { return _count; }

void IncRefCount () { _count++; }

int DecRefCount () { return --_count; }

private

int _count;

};

按照資本治理,一個引用計數是一種資本。假如你遵守它,你必要開釋它。當你意識到這一事實的時刻,剩下的就變得簡單了。簡單的遵照規則--再構造函數中得到引用計數,在析構函數中開釋。以致有一個RefCounted的smart pointer等價物:

template

class RefPtr

{

public:

RefPtr (T * p) : _p (p) {}

RefPtr (RefPtr & p)

{

_p = p._p;

_p->IncRefCount ();

}

~RefPtr ()

{

if (_p->DecRefCount () == 0)

delete _p;

}

private

T * _p;

};

留意模板中的T不比成為RefCounted的后代,然則它必須有IncRefCount和DecRefCount的措施。當然,一個便于應用的RefPtr必要有一個重載的指針造訪操作符。在RefPtr中加入轉換語義學(transfer semantics)是讀者的事情。

所有權收集

鏈表是資本治理闡發中的一個很故意思的例子。假如你選擇表成為鏈(link)的所有者的話,你會陷入實現遞歸的所有權。每一個link都是它的承襲者的所有者,并且,響應的,余下的鏈表的所有者。下面是用smart pointer實現的一個表單元:

class Link

{

// ...

private

auto_ptr _next;

};

最好的措施是,將連接節制封裝到一個弄構進行資本轉換的類中。

對付雙鏈表呢?安然的做法是指明一個偏向,如forward:

class DoubleLink

{

// ...

private

DoubleLink *_prev;

auto_ptr _next;

};

留意不要創建環形鏈表。

這給我們帶來了別的一個有趣的問題--資本治理可以處置懲罰環形的所有權嗎?它可以,用一個mark-and-sweep的算法。這里是實現這種措施的一個例子:

template

class CyclPtr

{

public:

CyclPtr (T * p)

:_p (p), _isBeingDeleted (false)

{}

~CyclPtr ()

{

_isBeingDeleted = true;

if (!_p->IsBeingDeleted ())

delete _p;

}

void Set (T * p)

{

_p = p;

}

bool IsBeingDeleted () const { return _isBeingDeleted; }

private

T * _p;

bool _isBeingDeleted;

};

留意我們必要用class T來實現措施IsBeingDeleted,就像從CyclPtr承襲。對特殊的所有權收集民眾化是十分直接的。

將原有代碼轉換為資本治理代碼

假如你是一個履歷富厚的法度榜樣員,你必然會知道找資本的bug是一件揮霍光陰的苦楚的經歷。我不必說服你和你的團隊花費一點光陰來認識資本治理是十分值得的。你可以急速開始用這個措施,無論你是在開始一個新項目或者是在一個項目的中期。轉換不必急速整個完成。下面是步驟。

首先,在你的工程中建立基礎的Strong Pointer。然后經由過程查找代碼中的new來開始封裝裸指針。

最先封裝的是在歷程中定義的臨時指針。簡單的將它們調換為auto_ptr并且刪除響應的delete。假如一個指針在歷程中沒有被刪除而是被返回,用auto_ptr調換并在返回前調用release措施。在你做第二次通報的時刻,你必要處置懲罰對release的調用。留意,縱然是在這點,你的代碼也可能加倍"精力充實"--你會移出代碼中潛在的資本透露問題。

下面是指向資本的裸指針。確保它們被自力的封裝到auto_ptr中,或者在構造函數平分配在析構函數中開釋。假如你有通報所有權的行徑的話,必要調用release措施。假如你有容器所有工具,用Strong Pointers從新實現它們。

接下來,找到所有對release的措施調用并且盡力清除所有,假如一個release調用返回一個指針,將它改動傳值返回一個auto_ptr。

重復著一歷程,直到著末所有new和release的調用都在構造函數或者資本轉換的時刻發生。這樣,你在你的代碼中處置懲罰了資本透露的問題。對其他資本進行相似的操作。

你會發明資本治理清除了許多差錯和非常處置懲罰帶來的繁雜性。不僅僅你的代碼會變得精力充實,它也會變得簡單并輕易掩護。

免責聲明:以上內容源自網絡,版權歸原作者所有,如有侵犯您的原創版權請告知,我們將盡快刪除相關內容。

您可能還會對下面的文章感興趣:

快三平台开户