目录
1.引言
有这样一个函数,用来揭示程序处理的优先权。另一个函数用来在某动态分配得到的Widget上进行某些带有优先权的处理:
int priority();
void processWidget(std::trl::shared_ptr<Widget> pw,int priority);//依据”以对象管理资源“的原则,决定对动态分配的来的Widget运用智能指针,这里采用trl::shared_ptr
现在考虑调用processWidget:
processWidget(new Widget,priority());
该调用形式其实不能通过编译。因为trl::shared_ptr构造函数需要一个原始指针(raw pointer),但是该构造函数是个explicit构造函数,无法进行隐式转换,将得自"new Widget"的原始指针转换为processWidget所要求的trl::shared_ptr。
要想通过编译,写成如下形式:
processWidget(std::trl::shared_ptr<Widget>(new Widget),priority());
虽然这样能顺利通过编译,并且使用了”以对象管理资源“,但这样调用却可能出现资源泄露。具体原因在下节将进行分析。
2.分析
2.1编译不通过原理
编译器生成一个processWidget调用码之前,首先会核算即将被传递的各个实参。processWidget的第二个实参只是一个单纯的对priority函数的调用,但第一个实参std::trl::shared_ptr<Widget>(new Widget)由两部分组成:
1.执行"new Widget"表达式
2.调用trl::shared_ptr构造函数
于是在调用processWIdget之前,编译器必须创建代码,需要做以下三件事:
1.调用priority
2.执行"new Widget"
3.调用trl::shared_ptr构造函数
但这里存在一个问题,即C++编译器会以什么样的次序完成这些事情呢?这里可以确定的是"new Widget"一定执行于trl::shared_ptr构造函数被调用之前,因为这个表达式的结果还要被传递作为trl::shared_ptr构造函数的一个实参,但是对priority的调用则可以排在第一或第二或第三执行。
假设编译器选择以第二顺位执行它,最终的操作顺序如下:
1.执行”new Widget“
2.调用priority
3.调用trl::shared_ptr构造函数
倘若在这个过程中,对priority的调用导致异常,会发生什么事情呢?在这种情况下”new Widget“返回的指针将会遗失。因为它还未执行第三步,尚未被置入trl::shared_ptr内,而该步是用来防止资源泄露的武器。正因为如此,在对processWidget的调用过程中,可能会引发资源泄露。因为在资源被创建(由new Widget)和资源被转换为资源管理对象这两个事件点之间可能发生异常干扰。
2.2解决方法
避免这类方法其实很简单:
- 创建Widget
- 将它置入一个智能指针内,然后将那个智能指针传给processWidget;
std::trl::shared_ptr<Widget> pw(new Widget);//以智能指针存储newed所得的对象作为一条单独的语句
processWidget(pw,priority());//这个调用动作不会造成资源泄露
以上之所以行得通,因为编译器都是一步一步按照编写顺序对代码进行执行,不会跨越语句进行各项操作。在上述的代码中,"new Widget"及对"trl::shared_ptr"构造函数的调用操作这两个动作和对"priority函数的调用"是分开来的,即执行顺序已经被确定了。