[翻译] Effective C++, 3rd Edition, Item 15: 在 resource-managing classes(资源管理类)中提供对 raw resources(裸资源)的访问

Item 15: 在 resource-managing classes(资源管理类)中提供对 raw resources(裸资源)的访问

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

发布:http://blog.csdn.net/fatalerror99/

resource-managing classes(资源管理类)真是太棒了。他们是你防御 resource leaks(资源泄漏)的堡垒,没有这样的泄漏是设计良好的系统的基本特征。在一个完美的世界中,你可以在所有你与资源的交互中依赖这样的 classes,从来不需要因为对 raw resources(裸资源)的直接访问而玷污你的手。但是这个世界并不完美,很多 APIs 直接涉及资源,所以除非你打算坚决放弃使用这样的 APIs(很少成为现实的事),否则,你只得经常绕过 resource-managing object(资源管理对象)而直接处理 raw resources(裸资源)。

例如,Item 13 介绍的使用类似 auto_ptrtr1::shared_ptr 这样的 smart pointers(智能指针)来持有对类似 createInvestment 这样的 factory function(工厂函数)的调用的结果想法:

std::tr1::shared_ptr<Investment> pInv(createInvestment());  // from Item 13

假设你打算使用的一个函数与 Investment objects 一起工作时是这样的:

int daysHeld(const Investment *pi);        // return number of days
                                           // investment has been held

你打算像这样调用它,

int days = daysHeld(pInv);                 // error!

但是这代码不能编译:daysHeld 要求一个裸的 Investment* 指针,但是你传给它一个类型 tr1::shared_ptr<Investment> 的 object。

你需要一个将 RAII class(当前情况下是 tr1::shared_ptr)的 object 转化为它所包含的 raw resource(裸资源)(例如,底层的 Investment*)的方法。有两个常规方法可以做到:explicit conversion(显式转换)和 implicit conversion(隐式转换)。

tr1::shared_ptrauto_ptr 都提供一个 get member function(成员函数)用于实行一次 explicit conversion(显示转换),也就是,用于返回 smart pointer object(智能指针对象)内部的raw pointer(裸指针)(的一个拷贝):

int days = daysHeld(pInv.get());           // fine, passes the raw pointer
                                           // in pInv to daysHeld

就像实际上的所有 smart pointer classes(智能指针类)一样,tr1::shared_ptrauto_ptr 也都重载了 pointer dereferencing operators(指针解引用操作符)(operator->operator*),而这样就允许到底层 raw pointers(裸指针)的 implicit conversion(隐式转换):

class Investment {                                 // root class for a hierarchy
public:                                            // of investment types
  bool isTaxFree() const;
  ...
};

Investment* createInvestment();                    // factory function

std::tr1::shared_ptr<Investment>                   // have tr1::shared_ptr
  pi1(createInvestment());                         // manage a resource

bool taxable1 = !(pi1->isTaxFree());               // access resource
                                                   // via operator->
...
std::auto_ptr<Investment> pi2(createInvestment()); // have auto_ptr
                                                   // manage a
                                                   // resource

bool taxable2 = !((*pi2).isTaxFree());             // access resource
                                                   // via operator*

...

因为有些时候有必要取得 RAII object 内部的 raw resource(裸资源),所以一些 RAII class 的设计者就通过提供一个 implicit conversion function(隐式转换函数)来给刹车抹油。例如,考虑以下这个 RAII class,它要为一个 C API 提供原始状态的 fonts(字体):

FontHandle getFont();                      // from C API—params omitted
                                           // for simplicity

void releaseFont(FontHandle fh);           // from the same C API
class Font {                               // RAII class
public:
  explicit Font(FontHandle fh)             // acquire resource;
  : f(fh)                                  // use pass-by-value, because the
  {}                                       // C API does

  ~Font() { releaseFont(f); }              // release resource

private:
  FontHandle f;                            // the raw font resource
};

假设有一个巨大的 font-related(字体相关)的 C API 只能与 FontHandle 打交道,这就频繁地需要将 Font objects 转换为 FontHandles。Font class 可能提供一个像 get 这样的 explicit conversion function(显示转换函数):

class Font {
public:
  ...
  FontHandle get() const { return f; }     // explicit conversion function
  ...
};

不幸的是,这就要求客户每次想要与这个 API 打交道时都要调用 get

void changeFontSize(FontHandle f, int newSize);     // from the C API

Font f(getFont());
int newFontSize;
...

changeFontSize(f.get(), newFontSize);               // explicitly convert
                                                    // Font to FontHandle

一些程序员可能发现对这个转换的显式请求的需要令人郁闷到足以避免使用这个 class。反过来,这又增加了 leaking fonts(字体泄漏)的机会,而这每一件事都是通过设计 Font class 来避免的

可选择的办法是为 Font 提供一个到它的 FontHandle 的 implicit conversion function(隐式转换函数):

class Font {
public:
  ...
  operator FontHandle() const { return f; }        // implicit conversion function
  ...
};

这样就可以使对 C API 的调用简单而自然:

Font f(getFont());
int newFontSize;
...

changeFontSize(f, newFontSize);            // implicitly convert Font
                                           // to FontHandle

不利的方面是 implicit conversions(隐式转换)增加了错误的机会。例如,一个客户可能会在想要使用 Font 的地方意外地创建一个 FontHandle

Font f1(getFont());

...

FontHandle f2 = f1;                        // oops! meant to copy a Font
                                           // object, but instead implicitly
                                           // converted f1 into its underlying
                                           // FontHandle, then copied that

现在,程序有了一个被 Font object f1 管理的 FontHandle,但是这个 FontHandle 也能通过直接使用 f2 来加以利用。这几乎绝对不会是什么好事。例如,当 f1 被销毁,字体将被释放,f2 则被 dangle(悬挂)。

关于是否提供从一个 RAII class 到它的底层资源的 explicit conversion(显式转换)(例如,通过一个 get member function(成员函数))或者是否允许 implicit conversion(隐式转换)的决定,要依靠 RAII class 被设计履行的具体任务和它被计划使用的情境而做出。最好的设计很可能就是坚持 Item 18 的建议(使 interfaces(接口)易于正确使用,而难以错误使用)的那一个。通常,一个类似 get 的 explicit conversion function(显式转换函数)是更可取的方式,因为它将意外的 type conversions(类型转换)的机会减到最少。然而有时,通过 implicit type conversions(隐式类型转换)提高使用的自然性将使天平向那个方向倾斜。

你可能已经意识到,函数返回一个 RAII class 内部的 raw resource(裸资源)违背了 encapsulation(封装性)。这是正确的,但这并非像它开始看上去那样是个设计的祸患。RAII classes 的存在并非为了封装什么东西;它的存在是为了确保一个特定的动作—— resource release(资源释放)——的发生。如果你希望,资源的 encapsulation(封装性)的地位也可以提高到这个主要功能之上,但这并非必需。此外,一些 RAII classes 将实现的严格封装性和底层资源的非常宽松的封装性结合在一起。例如,tr1::shared_ptr 整体封装了它的 reference-counting machinery(引用计数机制),但它依然提供对它所包含的 raw pointer(裸指针)的简单访问。就像大多数设计良好的 classes,它隐藏了客户不需要看到的,但它也让客户的确需要访问的那些东西可以利用。

Things to Remember

  • APIs 经常需要访问 raw resources(裸资源),所以每一个 RAII class 都应该提供一种方法以取得它所管理的资源。
  • 访问可以经由 explicit conversion(显式转换)或者 implicit conversion(隐式转换)进行。通常,explicit conversion(显式转换)更安全,而 implicit conversion(隐式转换)对客户来说更方便。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值