分享
 
 
 

C++箴言:在资源管理类中准备访问裸资源

王朝c/c++·作者佚名  2008-06-01
窄屏简体版  字體: |||超大  

资源治理类真是太棒了。他们是你防御资源泄漏的防波堤,没有这样的泄漏是设计良好的系统的基本特征。在一个完美的世界中,你可以在所有与资源的交互中依靠这样的类,从来不需要因为直接访问裸资源(raw resources)而玷污你的手。但是这个世界并不完美,很多 API 直接涉及资源,所以除非你计划果断放弃使用这样的 API(这种事很少会成为实际),否则,你就要经常绕过资源治理类而直接处理裸资源(raw resources)。

例如,以前介绍的使用类似 auto_ptr 或 tr1::shared_ptr 这样的智能指针来持有调用类似 createInvestment 这样的 factory 函数的结果: std::tr1::shared_ptr<Investment> pInv(createInvestment());

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

int daysHeld(const Investment *pi); // return number of days

// investment has been held

你打算像这样调用它,

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

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

你需要一个将 RAII 类(当前情况下是 tr1::shared_ptr)的对象转化为它所包含的裸资源(例如,底层的 Investment*)的方法。有两个常规方法来做这件事。显式转换和隐式转换。

tr1::shared_ptr 和 auto_ptr 都提供一个 get 成员函数进行显示转换,也就是说,返回一个智能指针对象内部的裸指针(raw pointer)(或它的一个副本):

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

// in pInv to daysHeld

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

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 对象内部的裸资源,所以一些 RAII 类的设计者就通过提供一个隐式转换函数来给刹车抹油。例如,考虑以下这个 RAII 类,它要为 C++ API 提供原始状态的字体资源:

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

};

假设有一个巨大的与字体有关的 C++ API 只能与 FontHandle 打交道,这就需要频繁地将 Font 对象转换为 FontHandle。Font 类可以提供一个显式的转换函数,比如 get:

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

一些程序员可能发现对显式请求这个转换的需求足以令人郁闷而避免使用这个类。反过来,设计 Font 类又是为了预防泄漏字体资源的机会的增长。

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

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

不利的方面是隐式转换增加了错误的机会。例如,一个客户可能会在有意使用 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 对象 f1 治理的 FontHandle,但是这个 FontHandle 也能通过直接使用 f2 来加以利用。这几乎绝对不会成为什么好事。例如,当 f1 被销毁,字体将被释放,f2 则被悬挂(dangle)。

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

你可能已经意识到,函数返回一个 RAII 类内部的裸资源破坏了封装。这是正确的,但这并非像它开始看上去那样是个设计的祸患。RAII 类的存在并非为了封装什么东西;它的存在是为了确保一个非凡的动作——资源释放——的发生。假如你希望,封装资源的地位也可以提高到这个主要功能之上,但这并非必需。此外,一些 RAII 类将实现的真正封装和底层资源的非常宽松的封装结合在一起。例如,tr1::shared_ptr 封装了它的引用计数的全部机制,但它依然提供对它所包含的资源的简单访问。就像大多数设计良好的类,它隐藏了客户不需要看到的,但它也让客户的确需要访问的那些东西可以利用。

Things to Remember

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

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有