WQ翻译“啊”这一个小时内我第七次受挫后的大叫。 温迪明显不胜其扰了。”瞧瞧朋友安静一下好吧”她的声音飘过隔间。我觉得她的反应比以前好多了。我以前就和auto_ptrs搏斗过了。在为Guru干活的最初几天内我已经领教过auto_ptrs的价值了以及它那不显眼的陷阱[1]. 我已经被auto_ptr那奇怪的所有权问题扎过一次了。auto_ptr使用伪装为拷贝操作的移交操作。想像一下这种情况: 你在操作一个复印机放上你的表格然后按 “拷贝”按钮。过了一会儿复印机给你副本然后将原件送入碎纸器。这就是auto_ptr的拷贝和赋值操作干的好事。无论如何我现在已经更聪明和更富有经验了可仍然正陷入在auto_ptr的形形色色的限制之中。比如说auto_ptr不能用来包容指向数组的指针。我玩弄了一个Hacking的技巧来绕开它通过使用显式的数组deleteauto_ptrT autoArray(new T[10]);//...delete [] autoArray.release;()但立刻就后悔了。我可以预见到这个 “面目可憎的东西”(Guru会也会这么称呼它) 的许多危险。最重要的是嗯auto_ptr的卖点就是拥有和销毁内存手工这么做完全打破了使用它的本意。因此,我决定改用一个vector来拥有数组。它比我所需要的还更强劲一些因为我不需要动态更改vector的大小而它提供了此功能。 我实际上想要是一个能自动处理指针和内存管理的灵巧指针并且可以用来处理数组。“使用Boost库, 天行者”我听到身后Guru的声音。“是不是‘使用大场天行者’” 我侧肩答道。 “而且不要叫我天行者。”“Boost是能量的源泉”Guru继续于她的电影台词之中。我今天没心情于是我打断了她。”嗯我们能今天不用演戏吗鲍伯在伦敦的办公室里也没有新员神情反常。”她来到附近我看见了她的微笑和耸肩。”哦我仅仅感到无聊”她叹了口气。”你看过Boost库中的灵巧指针了吗”“嗯”我吞吞吐吐的”我还没空仔细去看Boost库。它怎么实现灵巧指针的”“共有五种灵巧指针”Guru一边坐下一边说着。”两个处理指针的单所有权两个处理共享所有权。”“共享所有权哦类似于带引用计数的类”“完全正确。灵巧指针是成对的一个处理指向单一物体的指针另一个处理指向数组的指针。”在说的时候她在白板上写下scoped_ptrscoped_arrayshared_ptr和 shared_array。“只有四个你前面说有五个”我问。“剩下的一个叫weak_ptr。它是shared_ptr的非拥有关系的观察者。我随后讲它。 scoped_*形灵巧指针在它们离开生存范围时自动析构所指向的对象。一个提议用途是实现Pimpl惯用法指向实现的指针”在我发问前她急忙加了一句。“如此说来...他们象auto_ptr对吧”“不十分象。scoped_ptr和scoped_array不可拷贝。”“不可拷贝那么如果我将一个指针作为类的成员”我边说边在白板上潦草地写着class T{scoped_ptrTImpl p;...“我如何实现拷贝和赋值”“和auto_ptr一样”Guru边答边写T( const T other ): p(new TImpl(*other.p){}T operator( const T other ){scoped_ptrTImpl tmp(new TImpl(*other.p));p.swap;(tmp)}“Ooookay”我懒洋洋地说着装作我已经懂了。“ 于是当我使用scoped_ptr的时候, 我必须同样完成使用auto_ptr时所必须做的所有工作对吧那么为什么不就使用auto_ptr”“因为使用auto_ptr时能编译但是做错事, 因为自动生成的函数。 使用 scoped_ptr 使得极难忽略拷贝语义因为如果你使用编辑器自动生成的版本时编辑器将会拒绝编译这个类。同样scoped_ptr不能够在一个不完全的类型上被使用。”我给予了Guru我擅长的车前灯前的鹿的目光。 她叹了口气。“考虑这种情况”她边说边在白板上写:class Simple;Simple * CreateSimple();void f(){auto_ptrSimpleautoS(CreateSimple());scoped_ptrSimplescopedS(CreateSimple());//...}“在函数体中Simple是一个不完全的类型。如果它的析构函数是有行为的那么通过auto_ptr或scoped_ptr销毁它其结果为未定义。一些编译器在你实例化auto_ptr时将会警告你这一点但不是所有编译器都会这么做。相对的scoped_ptr为确保语义正确而做了些小动作以造成编译错误如果是在实例化一个不完全类型的话。”“哦是的当然” 我来劲了以显示我理解了。 “因此我们应该始终使用scoped_ptr来代替auto_ptr对吧”“错抱歉” 她失望地说道。“auto_ptr仍然有它的用处。不像auto_ptrscoped_ptr 没有release()成员它不能够放弃指针的所有权。 因为这个以及因为scoped_ptr不能够被复制当scoped_ptr离开生存范围时它所管理的指针总是被delete。这使得scoped_ptr不适用于需要传递所有权的地方比如厂。”“使用scoped_ptr向其他程序员表明你的意图。它告诉其他人‘这个指针不应该在当前范围外被复制’。正相反auto_ptr允许从产生指针的代码空间向外传递所有权但是它维持对指针的控制除非所有权的传递是完全地。 这当然对写异常安全的代码有重要意义。”“啊好我明白了”我嘟囔着。“ 你提到的另一种灵巧指针是怎么回事”“scoped_array的行为和scoped_ptr相同除了它处理是对象数组而不是单个对象。 scoped_array 提供了稍有差别的访问函数。它没有operator*或operator-但它有operator[]。我相信”她总结道“你需要是一个 scoped_array。”“也许吧”我答道”但是我认为在作决定之前我应该多了解些shared_*形的灵巧指针。”“非常明智我的徒……弟抱歉叫习惯了。”Guru歉意地笑了一下。“是的, 熟悉各个选择之后再作决定是个好习惯。”她指着白板上的shared_ptr 和 shared_array说道“shared_*形的灵巧指针是非常有用的工具。 他们是引用计数型的指针能够区分出所有者和观察者。举例来说, 使用上面的class T”void sharing(){shared_ptrT firstShared(new T);shared_ptrT secondShared;(firstShared)//...}“两个shared_ptr对象指向相同的T对象。引用计数现在是2。既然shared_ptr是引用计数的对象一直不被销毁直到最后一个shared_ptr离开生存范围此时引用计数降为0。shared_*的灵巧指针有相当的柔性你可以使用它们包容指向不完全类型的指针。”“我想你说过的这很糟吧”“是很糟如果你不小心的话。 shared_*的灵巧指针可以用一个指向不完全类型的指针来实例化但必须要指明一个函数或函数子供销毁被拥有对象时调用。比如说我们修改了上面的f函数”void DestroySimple( Simple* );void f(){shared_ptrSimplesharedSFails( CreateSimple() );shared_ptrSimplesharedSSucceeds( CreateSimple(), DestroySimple );}“第一个shared_ptr失败了, 因为Simple是一个不完全类型。第二个成功了因为你明确地告诉了 shared_ptr 该如何销毁这个指针。“因为 shared_ 指针被设计可被拷贝它们完全适用于标准容器, 包括associative 容器。并且在那些必须强制类型转换的特别场合上可以定义特别的类型转换操作以生成新的shared_*形的灵巧指针。 例如如果我们有一个类Base以及公有继承而来的Derived和一个无关类那么你将遇到”void g(){shared_ptrBasesharedBase( new Derived );shared_ptrDerived sharedDer shared_dynamic_castDerived( sharedBase);shared_ptrUnrelated sharedUnrelated shared_dynamic_castUnrelated( sharedBase);try{shared_ptrUnrelated oops shared_polymorphic_castUnrelated( sharedBase);}catch( bad_cast){//...}}“同样还存在着一个shared_static_cast供那些特别场合使用”Guru总结道。我对这个函数研究了一会儿。“好吧让我试试是否能推算出将发生什么。第一个shared_dynamic_cast在灵巧指针上执行了一个dynamic_cast返回一个新的灵巧指针。 假如dynamic_cast失败了怎么办在引用计数上将发生什么”Guru满意地点点头。“在那种情况下原始的计数不被影响而且新的hared_ptr包容的是一个NULL指针。正如你从try/catch语句推测的shared_polymorphic_cast将试图在被容纳的指针上执行dynamic_cast。 如果转换失败它将抛出bad_cast异常。”“哇”我赞叹道“这个类可真完备。看起来很不错。”“的确”Guru同意。 “但还有些事情是必须要知道的。引用计算过于简单而不能检测任何形式的循环引用。同样shared_ptr也没有实现任何形式的写时拷贝copy-on-write因此用它来实现Pimpl惯用法时必须仔细衡量。”我深思熟虑后确定我喜欢它。“嗨”我追问道我还记着呢“你提到过的weak_ptr是怎么回事”“啊是的。weak_ptr与shared_ptrs联合使用。weak_ptr是观察者不影响共享对象的引用计数。weak_ptr的主要目的是允许shared_ptr参与循环依赖A引用BB反过来又引用A。我喜欢将它想象成俱乐部中的准会员它没有投票权但是能参加俱乐部的会议。”“Hmmm……” 我仔细想了一下。“因为 weak_ptr不影响引用计数如果俱乐部解散了怎么办也就是说最後一个shared_ptr离开了生存范围而此时 weak_ptr正在使用共享对象”“在那情况下weak_ptr维护的指针被设置为NULL。而对于NULL不能进行任何dereferencing操作例如operator*、operator-、或operator[]在dereferencing指针前应该进行NULL检查。于是正如dereferencing内建指针前必须进行NULL检查你也一定要检查灵巧指针是否为NULL。这个限制适用于Boost库中的所有灵巧指针。”“天那要记太多的东西了”我一边说一边盯着潦草地写在白板上的东西。“别担心”Guru微笑着站起离开了。“我会email给你Boost库的URL以及练习和示范各种不同指针的一个小程序。”她当然说到做到几分钟内我收到了她的email。 我读到这句时立即就闭上了眼睛“我的徒弟如前所说这是Boost库中的说明文档: [2]。 阅读并思考附上问题[3]。”[感谢]Thanks to Peter Dimov and Bjorn Karlsson for providing valuable comments and updates.[注释][1] Jim Hyslop and Herb Sutter. “Conversations #1,” C Report, April 2000.[2] The most recent version of the library and documentation can also be obtained via anonymous CVS checkout from the Source Forge project. Detailed instructions can be found at: .[3] The sample code can be downloaded from the CUJ website: hyslop.zip.