C++基于范围循环(range-based for loop)的陷阱
C++的基于范围的循环是C++11出现的新特性,很方便,一定程度上替代了使用迭代器的for循环用法。不过基于范围的for循环有一个隐藏的陷阱,如果不注意可能会出现严重的内存错误。
举例说明
看下面这个代码:
#include <iostream>
#include <string> using namespace std; struct MyClass
{
string text = "MyClass"; string& getText()
{
return text;
}
}; int main()
{
for (auto ch : MyClass().text)
{
cout << ch;
}
cout << endl;
}
这个代码很简单,输出结果就是 "MyClass"。但如果稍微修改第18行,改为以下的样子:
for (auto ch : MyClass().getText())
{
cout << ch;
}
结果什么都不会输出,程序直接退出。要理解为什么会出现这种行为,要先知道基于范围的for循环是怎么定义的。
基于范围的for循环定义
在C++11标准中,它有以下的格式
attr(optional) for ( range_declaration : range_expression ) loop_statement
其中attr是可选的,range_declaration部分相当于我们代码中的 "auto ch",range_expression部分相当于 "MyClass().getText()",loop_statement就是 "{ cout << ch; }"
标准规定,上面的循环表达式应当等价于
{
auto && __range = range_expression;
for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
其中begin_expr和end_expr由range_expression的类型来决定。
这里面值得注意的是,第一行声明的__range类型是 "auto &&",所以如果range_expression是右值的临时对象,则__range可以延长range_expression的生存期。
问题分析
看了给予范围的for循环的定义之后,前面例子中的问题出现的原因就很清楚了。
原始的例子中,range_expression是 "MyClass().text",MyClass()是临时对象,同时 "MyClass()" 这个表达式是右值。所以,"MyClass().text" 这个表达式也是右值,"MyClass().text" 这个对象是临时对象中的一部分。所以,在 "auto && __range = range_expression;" 这个语句中,auto会被推导为 "std::string"。初始化右值引用为临时对象的一部分时,可以延长整个临时对象的生存期,在引用被销毁时临时对象才会被销毁。所以for循环可以正常执行。
但是在修改过后,range_expression是 "MyClass().getText()"。同样地,MyClass()是临时对象,"MyClass()" 这个表达式是右值。但是 "getText()" 的返回类型为 "string&",所以,"MyClass().getText()" 这个表达式是左值。所以,在 "auto && __range = range_expression;" 这个语句中,auto会被推导为 "string &",语句等价于 "string & __range = range_expression;" 。虽然"MyClass().getText()" 这个对象是临时对象中的一部分,但是在初始化非const的左值引用时,不会延长临时对象的生存期,所以在这个初始化语句结束的同时MyClass()这个临时对象就被销毁了,__range成为了野引用,所以后面的循环语句可能会出现内存错误。
总结
基于范围的for循环非常方便,甚至可以遍历临时对象,在日常中也经常使用到。但是要注意的是,如果要遍历临时对象的话,需要遍历的临时对象必须是右值表达式,而且也要注意表达式中间产生的其他临时对象是在循环开始前就会被销毁的,只有表达式返回的最后的临时对象才会被“存”起来。
最新文章
- 51单片机tea5767收音机 红外遥控 自动搜台 存台 DIY
- [Android] HttpURLConnection &; HttpClient &; Socket
- js中return,this,arguments,currentStyle和getComputedStyle小析
- apache-flume-1.5.0-bin windows
- 大熊君说说JS与设计模式之------策略模式Strategy
- zw版【转发&#183;台湾nvp系列Delphi例程】HALCON FillUpShape1
- MySQL 体系结构以及各种文件类型学习汇总 (转)
- Computational Geometry Template_Polygon
- Struts2 本是非单例的,与Spring集成就默认为单例
- eclipse 僵死/假死 问题排查及解决
- poj3461Oulipo
- Linux内核分析(三)----初识linux内存管理子系统
- Xamarin.Android 调用Web Api(通过ListView展示远程获取的数据)
- quartz入门详解
- 团队项目(六)- Alpha阶段项目复审(江山代有才人秃)
- Mybatis项目中不使用代理写法【我】
- Log4j日志根据配置输出到多个自定义文件
- angular笔记_2
- 学习之响应式Web设计---一个实例
- binlog和redo log日志提交
热门文章
- ubuntu下动态链接库的编译和使用实例
- HI3531串口测试程序(arm)
- linux下FFmpeg编译生成ffplay
- Java中的换行符
- JavaScript获取当前值
- dpkg: error: -i (--install) 和 -i (--install) 两个操作之间有矛盾
- ArgumentError:Error #2004:某个参数无效
- Android动态类生成预加载-dexmaker使用
- CSA Round #54 $\ $Voting
- [BZOJ2761] [JLOI2011] 不重复数字 (set)