Effective C++读书笔记5

条款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;
};

Font 可以提供一个get函数,显示的转换为内部资源的指针:
class Font{
public:
	//...
	FontHandle get() const{
		
		return f;
	}
};

但是这样的转换不够直观和方便,所以产生了方法二。

方法二:隐式转换:

class Font{
public:
	operator FontHandle() const{  //隐式转换函数
		
		return f;
	}
};

这使得客户调用 C API时轻松自然:
Font f(getFont());
int newFontSize;
changeFontSize(f, newFontSize);

是否提供一个显示转换函数或者隐式转换函数,答案取决于RAII类被设计执行的特定工作,以及它被使用的情况。


请记住:

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:

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());

请记住:以独立语句将newed对象存储于只能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏。



你可能感兴趣的:(C++,读书,delete,Class,编译器,RAII)