大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C++技巧 > C++技巧:在资源管理类中准备访问裸资源

C++技巧:在资源管理类中准备访问裸资源

关键词:C裸资源  阅读(494) 赞(12)

[摘要]本文主要是对C++技巧:在资源管理类中准备访问裸资源的讲解,希望对您学习C++有所帮助!

资源管理类真是太棒了。他们是你防御资源透露的防波堤,没有这样的透露是设计良好的系统的基本特征。在一个完美的世界中,你可以在一切与资源的交互中依赖这样的类,历来不需求由于直接访问裸资源(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 类都应该提供取得它所管理的资源的方法。

·访问可以经过显式转换或许隐式转换中止。通常,显式转换更安全,而隐式转换对客户来说更方便。



相关评论