Effective C++学习笔记总链接
改善程序与设计的55个具体做法学习笔记-每日1条
条款15:在资源管理类中提供对原始资源的访问
【技巧】
1. API 往往要求访问原始资源(raw resources),所以每个RAII class应该提供一个“取得其所管理之资源”的办法。
2. 对原始资源的访问可能经由显示转换或隐式转换。一般而言显示转换比较安全,但隐式转换对客户比较方便。
资源管理类很棒,它们是你对抗资源泄漏的堡垒,排除此等泄漏是良好设计系统的根本性质。
资源管理类:通常是 RAII class(以对象管理资源,或者被称为资源取得时便是初始化时机),一般使用shared_ptr或者auto_ptr。
有时候你需要直接访问原始资源,这时候你需要一个函数可将RAII class 对象转化为其所内含之原始资源。有两种做法可以达成目标:显示转换和隐式转换
RAII访问原始资源–显示转换
使用get() 成员函数,用来执行显式转换,也就是它会返回智能指针内部的原始指针。
FontHandle getFont(); // 这是个C API
void releaseFont(FontHandle fh); //来自同一组C API
class Font //RAII class
{
public:
explicit Font(FontHandle fh) // 获得资源
: f(fh) //采用pass-by-value
{} //因为C API这样做
~Font()
{ releaseFont(f); } //释放资源
private:
FontHandle f; //原始(raw)字体资源
};
有的API需要处理FontHandle,Font class 可以为此提供一个显式转换函数,如下代码:
class Font
{
public:
...
FontHandle get() const{return f;} // 显式转换函数
...
};
不幸的是这使得客户每当想要使用API时就必须调用get():
void changeFontSize(FontHandle f,int newSize); // C API
Font f(getFont());
int newFontSize;
...
changeFontSize(f.get(), newFontSize); //显式地将Font转换为FontHandle
某些程序员可能认为,这般地到处要求显式转换,足以使人倒尽胃口,不愿意使用这个class
另一个办法是隐式转换
RAII访问原始资源–隐式转换
class Font
{
public:
...
operator FontHandle() const //隐式转换函数
{ return f; }
...
};
使得客户调用C API时比较轻松且自然
Font f(getFont());
int newFontSize;
...
changeFontSize(f, newFontSize); 将Font隐式转换为FontHandle
但这个隐式转换会增加错误发生机会。例如客户可能会在需要Font时意外创建一个FontHandle
Font f1(getFont());
...
FontHandle f2 = f1;
//原意是拷贝一个Font对象,却反而将f1隐式转换为其底部的FontHandle,然后才复制它。
总结
是否提供一个显式转换函数(例如 get()成员函数)将RAII class转换为其底部资源,或是应该提供隐式转换,答案主要取决于RAII class 被设计执行的特定工作,以及它被使用的情况。
通常get()显式转换比较受欢迎,因为它将“非故意之类型转换”的可能性最小化了。
然而有时,隐式类型转换所带来的“自然用法”更方便。
RAII class内的那个返回原始资源的函数,与“封装”发生矛盾。那是真的,但一般它谈不上是什么设计灾难。
RAII class 并不是为了封装某物而存在的,它们的存在是为了确保一个特殊行为-----资源释放-----会发生。
像多数设计良好的class一样,隐藏了客户不需要看的部分,但备妥客户需要的所有东西。