条款15:在资源管理类中提供对原始资源的访问
资源管理类很好,但是有许多API 直接指涉资源管理类所管理的资源,那么只能绕过资源管理对象直接访问原始资源。条款13:使用智能指针保存工厂函数如CreateInvestment的调用结果:
std::tr1::shared_ptr<Investment> pInv(CreateInvestment());
假设你希望以某个函数处理Investment对象,像这样:
int dayHeld(const Investment* pi);
如果这样调用:
int days = dayHeld(pInv);
会报错,因为daysHeld需要的是Investment*指针,你传给他的却是个类型为tr1::shared_ptr<Investment>的对象。这时需要一个函数可将RAII对象转换为其所内含之原始资源,有两种方法可以达成目标
方法一:显示转换
shared_ptr和auto_ptr都提供了一个get成员函数,用来执行显示转换:
int days = dayHeld(pInv.get());
shared_ptr和auto_ptr也重载了operator->和operator*:
class Investment{ public: bool isTaxFree() const; //... }; Investment* CreateInvestment(); std::shared_ptr<Investment> pi1(CreateInvestment()); bool taxable1 = !(pi1->isTaxFree()); std::auto_ptr<Investment> pi2(CreateInvestment()); bool taxable2 = !((*pi2).isTaxFree());
FontHandle getFont(); void releaseFont(FontHandle fh); class Font{ public: explicit Font(FontHandle fh): f(fh){} ~Font(){ releaseFont(f); } private: FontHandle f; };
class Font{ public: //... FontHandle get() const{ return f; } };
方法二:隐式转换:
class Font{ public: operator FontHandle() const{ //隐式转换函数 return f; } };
Font f(getFont()); int newFontSize; changeFontSize(f, newFontSize);
请记住:
1.API往往要求访问原始资源,所以每个RAII类应该提供一个取得其所管理之资源的方法。
2.对原始资源的访问可能经由显示转换或隐式转换。一般而言显示转换比较安全,但隐式转换对客户比较方便。
条款16:成对使用new和delete时要采取相同形式
请记住:
如果你在new表达式中使用[], 必须在相应的delte表达式中也使用[],如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[].
条款17:以独立语句将newed对象置入智能指针
int priotiry(); void processWidget(std::shared_ptr<Widget> pw, int priority);
processWidget(new Widget, priority());
不能通过编译,因为shared_ptr构造函数需要一个原始指针,但该构造函数是个explicit构造函数,无法进行隐式转换,所以需要这样:
processWidget(std::shared_ptr<Widget>(new Widget), priority());
但是这样可能造成资源泄漏,因为调用processWidget函数之前,编译器必须创建代码,做以下三件事:
1)调用priority
2)执行 new Widget
3)调用shared_ptr构造函数
C++编译器以什么样的次序完成不确定,不像java或者c#。所以可能的执行顺序是:
1)执行new Widget
2) 调用priority
3)调用shared_ptr构造函数
所以万一priority函数调用导致异常,那么new Widget返回的指针将会遗失,因为它尚未置入shared_ptr内,所以要避免这类问题的产生可以使用分离语句:
std::shared_ptr<Widget> pw(new Widget); processWidget(pw, priority());