其实这一点讲的要点比较隐晦,在平时编程的时候很难发现这个问题,但要是不注意却又是一个隐患,所以对这个要点的理解更应该养成是一种编程习惯。
首先书上给了一段代码作为例子
int priority(); void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);这个priority()函数是计算优先级的,就比如是操作系统中进程的优先级,然后下面的processWidget函数是一个处理函数,这个处理函数中第一个参数用到了一个智能指针,这个智能指针是用来管理动态分配的一个Widget,另一个参数是优先级。
现在考虑这样来调用这个函数
processWidget(new Widget, priority());但是 这样是通过不了编译的,原因在于智能指针的构造函数中,利用它指向的元素类型(raw type 这里就是Widget)来构造的函数是explicit的,这就意味着不能进行隐式转换,解释一下: 按照原本的想法,应该是先new Widget得到一个指向Widget的指针Widget*,然后用这个指针去构造shared_ptr< Widget > pw, 显然要进行一个类型转换,但是问题就是这个构造函数是explicit修饰的,explicit的意义是在编译阶段就阻止进行隐式转换,所以无法通过编译,那怎么来解决这个问题呢? 很简单,写成这样就可以了
processWidget(std::tr1::shared_ptr<Widget>( new Widget) , priority());也就是在调用的时候写一个类型转换,就能通过编译。
但是就是这样一种写法,也会带来内存泄漏的隐患,这也是item17想强调的地方
来分析一下这种写法到底发生了什么,首先它的参数中,肯定是有这样的三个过程要进行:
这三个过程是在一个函数调用中发生的,在C++中没有规定这三个过程的发生顺序,也就意味着可以有这样的一种发生顺序:
如果按照这样的顺序进行操作,那么在进行到第二步priority()时,如果发生了异常,那么后面的第三步就不会再执行,而第三步是对动态分配内存管理的至关重要的方法(在item13中强调过),这样就导致我们会丢失对new产生的内存的泄漏。 那么如何来解决这个问题呢?书上的建议就是把过程1和3与过程2分离开来,具体来说就是
std::tr1::shared_ptr<Widget> pw(new Widget); processWidget(pw , priority());这样第一句完成new Widget的过程,同时把它进行类型转换,放进智能指针管理,然后再调用process函数进行处理,这样写成两句代码的形式就避免了内存泄漏的问题。这也就是item17强调的点 Store new ed objects in smart pointers in standalone statements(在一个单独的语句中把new创建的对象存进智能指针)