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

本文探讨了C++中使用智能指针管理动态分配对象时可能出现的资源泄漏问题。当在函数调用中直接将new操作与智能指针构造结合时,由于编译器可能的代码重排,可能导致异常情况下资源未被正确管理。解决方案是将new操作和智能指针的构造分开成两个独立语句,以确保资源安全。遵循这一原则能有效防止内存泄漏,确保代码的健壮性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Effective C++条款17:以独立语句将newed对象置入智能指针(Store newed objects in smart pointers in standalone statements)


《Effective C++》是一本轻薄短小的高密度的“专家经验积累”。本系列就是对Effective C++进行通读:


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

1、使用对象管理资源仍然会出现资源泄漏的用法

  假设我们有个函数返回处理程序的优先权,然后用另一个函数在某动态分配所得的Widget上进行某些带有优先权的处理

int priority();
//返回优先权
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority); //根据优先权处理对象

  我们知道 条款13的至理名言是以对象管理资源,processWidget为它需要处理的动态分配对象Widget使用了智能指针(tr1::shared_ptr)。

  现在考虑对processWidget函数的调用:

processWidget(new Widget, priority());

  这个函数调用不能通过编译,因为在tr1::shared_ptr构造函数需要一个原始指针,但该构造函数是个explicit构造函数,无法进行隐式转换,也就是不能将“new Widget”返回的原始指针直接隐式转换为processWidget需要的tr1::shared_ptr。下面的代码将会通过编译:

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

  虽然这里我们使用了对象类管理资源,但是这个调用仍然可能出现内存泄漏。如何才能出现资源泄漏呢?

2、智能指针资源泄漏情况

  在编译器生成一个对processWidget的调用之前,它们必须对函数参数做一些检查。第二个参数只是调用了函数priority,但是第一个参数包含两部分:

  • 执行new Widget

  • 调用tr1::shared_ptr构造函数

在调用processWidget之前,编译器必须为下面的三个步骤生成代码:

  • 调用priority

  • 执行 “new Widget”

  • 调用tr1::shared_ptr构造函数。

  对于上面三个步骤的执行顺序,c++编译器被给予了很大的弹性。(这同java和c#不同,这两门语言的执行顺序固定)“new Widget”表达式必须在tr1::shared_ptr构造函数之前被调用,因为它的结果会传递给tr1::shared_ptr作为参数,但是对priority()函数的执行次序是任意的(第一个,第二个,第三个执行都可以)。如果编译器选择第二个执行(因为这样可能会生成更高效的代码),执行顺序如下:

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

  试想,如果调用priority时产生异常将会发生什么?在这种情况下,从”new Widget”返回的指针会被丢失,因为它没有存入tr1::shared_ptr中,但我们的原意是使用tr1::shared_ptr来防止资源泄漏。对processWidget的调用会使资源泄漏发生,因为在资源被创建和将资源转交给资源管理对象的时间间隔内发生了异常。

3、如何避免资源泄漏

  在上面的分析中,我们可以看到在“资源创建(new)”和“资源被使用”之间如果发生了异常,那么就会造成资源泄漏。

  • 解决办法:避免这类问题就是分离语句,将“创建的对象”与“放入智能指针对象”这两个步骤合成一步完成,而不是在函数调用中完成,例如,下面的函数调用就不会产生错误:
std::tr1::shared_ptr<Widget> pw(new Widget); //以单独语句存储对象
processWidget(pw, priority()); //安全调用函数

  上面这种方法之所以行得通的,是因为编译器被给予更少的余地来对语句进行重新排序。在上面的代码中,我们将“new Widget”以及对tr1::shared_ptr构造函数的调用放在一个语句中,把对priority的调用放在另一个语句中,这样就不允许编译器在”new Priority”和tr1::shared_ptr构造函数之间执行priority。

5、牢记

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

总结

期待大家和我交流,留言或者私信,一起学习,一起进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写Bug那些事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值