Effective C++之条款16、17:成对使用new和delete时要采取相同形式、以独立语句将newed对象置入智能指针

声明:

  1. 文中内容收集整理自《Effective C++(中文版)第三版》,版权归原书所有。
  2. 本内容在作者现有能力的基础上有所删减,另加入部分作者自己的理解,有纰漏之处敬请指正。

Effective C++之条款16:成对使用new和delete时要采取相同形式

Use the same form in corresponding uses of new and delete.

请记住:

如果你在new表达式中使用[],必须在相应的delete表达式中也使用[]。如果你在new表达式中不使用[],则一定不要在相应的delete表达式中使用[]。

条款17:以独立语句将newed对象置入智能指针

Store newed objects in smart pointers in standalone statements.

假设有一个函数用来揭示处理程序的优先权,另一个函数对一个动态分配的Widget进行某些带有优先级的处理:

int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);

由于谨记“以对象管理资源”(条款13)的智慧名言, processWidget 决定对动态分配得来的Widget运用智能指针。

 progressWidget 的一次调用如下:

processWidget(new Widget, priority());

等等,这样调用将不会通过编译。 tr1::shared_ptr 的构造函数需要一个原始指针(raw pointer),而该构造函数是个explicit构造函数,无法进行隐式转换,将得自“ new Widget ”的原始指针转换为 processWidget 所需的 tr1::shared_ptr。下面的代码将顺利通过编译:

processWidget(std::tr1::shared_ptr<Widget>(new Widget),priority());

令人惊讶的是,虽然在此我们使用“对象来管理资源”,上述调用却可能泄漏资源。

编译器产生一个 processWidget 调用之前,必须首先核算被传入的各个实参。第二个参数仅仅调用了一个函数 priority ,但是第一个参数(“ std::tr1::shared_ptr<Widget>(newWidget) ”)包含两部分:

  1. 执行 “new Widget”表达式
  2. 调用 tr1::shared_ptr构造函数

 因此,在调用processWidget之前,编译器必须创建代码解决下面的三件事情:

  1. 调用 priority 
  2. 执行 “new Widget”
  3. 调用 tr1::shared_ptr 构造函数

C++编译器不会以特定次序执行这三件事情。可以确定的是执行 “new Widget”一定在调用 tr1::shared_ptr 构造函数之前。但是调用 priority 的工作可以放到第一,第二,也可以放在最后。如果编译器决定第二个处理它,我们就会得到这样的操作序列:

  1. 执行 “ new Widget ” 
  2. 调用 priority
  3. 调用 tr1::shared_ptr 构造函数

但是万一调用 priority 时抛出了一个异常,此情况下,由于“ new Widget ”返回的指针不会如愿保存在 tr1::shared_ptr 中,于是内存泄漏就发生了。在“资源被创建”和“资源被转换为资源管理对象”这段时间内,有如果发生异常,那么调用 processWidget 就会造成资源泄漏。

避免这类问题的办法很简单:使用分离语句分别写出:

  1. 创建Widget 并存入一个智能指针
  2. 把智能指针传递给 processWidget
std::tr1::shared_ptr<Widget> pw(new Widget);// 在单独的语句中创建Widget并存入一个智能指针
processWidget(pw, priority()); //这样调用就OK

以上之所以行得通,是因为编译器对“跨越语句的各项操作”没有重新排列的自由(只有在同一个语句内它才有那个自由度)。

请记住:

以独立语句将newed对象存储于智能指针内)。否则,一旦异常被抛出,有可能导致难以察觉的资源泄漏。

猜你喜欢

转载自blog.csdn.net/longmenshenhua/article/details/88796266