Effective C++读书笔记(9)(二)

2014-11-24 12:20:04 · 作者: · 浏览: 1
Held

就像几乎所有智能指针一样,tr1::shared_ptr和 auto_ptr 也都重载了指针解引用操作符(operator-> 和 operator*),它们允许隐式转换到底部原始指针:

class Investment { // investment继承体系的根类
public:
bool isTaxFree() const;
...
};
Investment* createInvestment(); // factory函数

std::tr1::shared_ptr pi1(createInvestment());

bool taxable1 = !(pi1->isTaxFree()); // 经由operator->访问资源
...

std::auto_ptrpi2(createInvestment());

bool taxable2 = !((*pi2).isTaxFree()); // 经由operator*访问资源

...

再考虑以下这个用于字体的RAII类(对C CPI而言字体是一种原生数据结构):

FontHandle getFont(); // C API,为求简化略参数

void releaseFont(FontHandle fh); // 来自同一组C API
class Font { // RAII class
public:
explicit Font(FontHandle fh): f(fh){} // 值传递获得资源

~Font() { releaseFont(f); }

private:
FontHandle f; // 原始字体资源
};

假设有大量与字体相关的C API,它们处理的是FontHandle,这就需要频繁地将 Font 对象转换为 FontHandle。Font 类可以提供一个显式的转换函数,比如get:
FontHandle get() const {return f; }

不幸的是,这就要求客户每次与 API 通信时都要调用 get:

void changeFontSize(FontHandle f, intnewSize); // C API

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

changeFontSize(f.get(), newFontSize); // 显式地将Font转换为FontHandle

一些程序员可能发现对显式请求这个转换的需求足以令人郁闷而避免使用这个类。另一个可选择的办法是为 Font 提供一个隐式转换函数,转型为FontHandle:
operator FontHandle()const { return f; }

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

changeFontSize(f, newFontSize); // 隐式转换,与上例作对照

不利的方面是隐式转换增加错误发生机会。例如,一个客户可能会在有意使用 Font 的地方意外地产生一个 FontHandle:

Font f1(getFont());

...

FontHandle f2 = f1;

// 原意是复制一个Font对象,却反而将f1隐式转换为其底部的FontHandle然后才复制

当 f1被销毁,字体将被释放,f2则被悬挂(dangle)。

最好的设计就是坚持“使接口易于正确使用,不易被误用”。通常,类似get的一个显式转换函数是更可取的方式,因为它将意外的类型转换的机会减到最少。而有时候通过隐式类型转换将提高使用的自然性。

RAII类的存在并非为了封装什么东西而是为了确保资源释放这一特殊行为的发生。此外,一些 RAII类将实现的真正封装和底层资源的宽松封装结合在一起如tr1::shared_ptr 封装了它引用计数的全部机制,但它依然提供对它所包含资源的简单访问。就像大多数设计良好的类,它隐藏了客户不需要看到的,但它也让客户确实需要访问的东西可以被利用。

· API 经常需要访问原始资源,所以每一个 RAII 类都应提供取得它所管理资源的方法。

· 访问可以通过显式转换或者隐式转换进行。通常,显式转换更安全,而隐式转换对客户来说更方便。


摘自 pandawuwyj的专栏