快捷搜索:  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退出,等等。一个"裸"指针,文件句柄,或者临界区状态没有所有者来确保它们的终极开释。基础的资本治理的条件便是确保每个资本都有他们的所有者。

第一规则

一个指针,一个句柄,一个临界区状态只有在我们将它们封装入工具的时刻才会拥有所有者。这便是我们的第一规则:在构造函数平分配资本,在析构函数中开释资本。

当你按照规则将所有资本封装的时刻,你可以包管你的法度榜样中没有任何的资本泄露。这点在当封装工具(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。)让我们从一个极其简单、枯燥但安然的器械开始。看下面的Smart 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

到今朝为止,我们所评论争论的不停是生命周期在一个零丁的感化域内的资本。现在我们要办理一个艰苦的问题--若何在不合的感化域间安然的通报资本。这一问题在当你处置惩罚容器的时刻会变得十分显着。你可以动态的创建一串工具,将它们寄放至一个容器中,然后将它们掏出,并且在终极安排它们。为了能够让这安然的事情--没有泄露--工具必要改变其所有者。

这个问题的一个异常显而易见的办理措施是应用Smart Pointer,无论是在加入容器前照样还找到它们今后。这是他若何运作的,你加入Release措施到Smart Pointer中:

template

T * SPtr::Release ()

{

T * pTmp = _p;

_p = 0;

return pTmp;

}

留意在Release调用今后,Smart Pointer就不再是工具的所有者了--它内部的指针指向空。现在,调用了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中。看下面的Expression措施的实现:

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(编码反省)

假如你严格遵循资本治理的条目,你就不会再资本泄露或者两次删除的地方碰到麻烦。你也低落了造访野指针的几率。同样的,遵照原有的规则,用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的调用都在构造函数或者资本转换的时刻发生。这样,你在你的代码中处置惩罚了资本透露的问题。对其他资本进行相似的操作。

你会发明资本治理清除了许多差错和非常处置惩罚带来的繁杂性。不仅仅你的代码会变得精力充实,它也会变得简单并轻易掩护。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

您可能还会对下面的文章感兴趣: