Effective C++第17条:要在单独的语句中使用智能指针来存储由new创建的对象
2011-07-03 13:26
274 查看
第17条: 要在单独的语句中使用智能指针来存储由new创建的对象
假设这里有一个函数用来显示处理优先级,另一个函数根据当前优先级为一个动态分配的 Widget 做一些处理:
int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);
一定要时刻记住“使用对象管理资源”这一真理(参见第 13 条)。 processWidget 中可以使用智能指针来动态分配其需要处理的 Widget 。
下面是对 progressWidget 的一次调用:
processWidget(new Widget, priority());
请稍等,不要试图这样调用。这将不会通过编译。 tr1::shared_ptr 的构造函数中包含了一个 explicit 的裸指针,于是便不存在从“ new Widget ”语句返回的裸指针到 processWidget 所需的 tr1::shared_ptr 的隐式转换。然而下边的代码将顺利通过编译:
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
看上去有些令人吃惊,尽管我们时时刻刻使用对象来管理资源,但是这里还是有出现资源泄漏的可能。了解这些发生的原由对我们深入理解是有一定帮助的。
在编译器能够生成对 processWidget 的调用之前,它必须评估传入的参数。第二个参数仅仅调用了一个函数 priority ,但是第一个参数(“ std::tr1::shared_ptr<Widget>(new Widget) ”)包含两部分:
运行 “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 中,因此它很有可能会丢失,于是内存泄漏就发生了。在资源被创建以后和这个资源转交给一个资源管理对象之前的这段时间内,有可能发生异常,如果发生的话,那么调用 processWidget 就会造成资源泄漏。
防止这类问题发生的办法很简单:使用单独的语句,创建 Widget 并将其存入一个智能指针,然后将这个智能指针传递给 processWidget :
std::tr1::shared_ptr<Widget> pw(new Widget);
// 在一个单独的语句中创建 Widget 并存入一个智能指针
processWidget(pw, priority()); // 这样调用就不会泄漏了。
这样是可行的,因为编译器为多行的语句安排执行顺序要比单一的语句时严格得多。由于这段改进的代码中,“ new Widget ”语句以及对 tr1::shared_ptr 的构造函数的调用在单独的语句中,对 priority 的调用在另一个单独的语句中,所以编译器就没有机会调换处理顺序了。
牢记在心
在单独的语句中使用智能指针来保存由new创建的对象。如果不这样做,你的程序会在抛出异常时发生资源泄漏。
假设这里有一个函数用来显示处理优先级,另一个函数根据当前优先级为一个动态分配的 Widget 做一些处理:
int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);
一定要时刻记住“使用对象管理资源”这一真理(参见第 13 条)。 processWidget 中可以使用智能指针来动态分配其需要处理的 Widget 。
下面是对 progressWidget 的一次调用:
processWidget(new Widget, priority());
请稍等,不要试图这样调用。这将不会通过编译。 tr1::shared_ptr 的构造函数中包含了一个 explicit 的裸指针,于是便不存在从“ new Widget ”语句返回的裸指针到 processWidget 所需的 tr1::shared_ptr 的隐式转换。然而下边的代码将顺利通过编译:
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
看上去有些令人吃惊,尽管我们时时刻刻使用对象来管理资源,但是这里还是有出现资源泄漏的可能。了解这些发生的原由对我们深入理解是有一定帮助的。
在编译器能够生成对 processWidget 的调用之前,它必须评估传入的参数。第二个参数仅仅调用了一个函数 priority ,但是第一个参数(“ std::tr1::shared_ptr<Widget>(new Widget) ”)包含两部分:
运行 “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 中,因此它很有可能会丢失,于是内存泄漏就发生了。在资源被创建以后和这个资源转交给一个资源管理对象之前的这段时间内,有可能发生异常,如果发生的话,那么调用 processWidget 就会造成资源泄漏。
防止这类问题发生的办法很简单:使用单独的语句,创建 Widget 并将其存入一个智能指针,然后将这个智能指针传递给 processWidget :
std::tr1::shared_ptr<Widget> pw(new Widget);
// 在一个单独的语句中创建 Widget 并存入一个智能指针
processWidget(pw, priority()); // 这样调用就不会泄漏了。
这样是可行的,因为编译器为多行的语句安排执行顺序要比单一的语句时严格得多。由于这段改进的代码中,“ new Widget ”语句以及对 tr1::shared_ptr 的构造函数的调用在单独的语句中,对 priority 的调用在另一个单独的语句中,所以编译器就没有机会调换处理顺序了。
牢记在心
在单独的语句中使用智能指针来保存由new创建的对象。如果不这样做,你的程序会在抛出异常时发生资源泄漏。
相关文章推荐
- Effective C++第17条:要在单独的语句中使用智能指针来存储由new创建的对象
- 【翻译】[Effective C++第三版•中文版][第17条]要在单独的语句中使用智能指针来存储由new创建的对象
- 第17条:以独立语句将new创建的对象存储在智能指针中
- 读书笔记 effective c++ Item 17 使用单独语句将new出来的对象放入智能指针
- 创建String对象时, 使用String s=new String ("abc")和String s="abc"语句有什么区别?
- new语句创建的对象,相当于使用一堆malloc
- 第2章 Java编程基础——FAQ2.22 创建String对象时, 使用String s=new String (“abc”)和String s=“abc”语句有什么区别?
- Java不能使用New去创建对象的情况
- JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
- [VB.NET]在VB.NET 2005中,如何创建Oracle的存储过程,以及如何来使用存储过程语句?
- 在spring 中如果使用new创建一个对象时 这个对象将不在受spring管理器管理
- 1.在使用new创建数组后,此时数组还是一个引用数组。 只有再创建新的对象,并把对象赋值给数组引用,到此初始化结束2.什么是引用类型?
- (转 )Delphi指针如何指向使用with开域语句创建的无名组件对象
- 使用MyBatis Generator自动创建表对应的dao层接口、模型层对象、sql语句的xml文件
- android程序使用百度地图sdk报错 错误出现在初始化创建对象mBMapManager = new BMapManager(context);
- 创建对象使用 new 和 alloc init的区别
- JavaScript中使用构造器创建对象无需new的情况说明
- JS中用new 创建对象及在构造函数中return的使用
- 使用new创建动态结构和自动, 静态, 动态三种存储方式
- JavaScript中使用构造器创建对象无需new的情况