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循环非常方便,甚至可以遍历临时对象,在日常中也经常使用到。但是要注意的是,如果要遍历临时对象的话,需要遍历的临时对象必须是右值表达式,而且也要注意表达式中间产生的其他临时对象是在循环开始前就会被销毁的,只有表达式返回的最后的临时对象才会被“存”起来。
C++基于范围循环(range-based for loop)的陷阱的更多相关文章
- 浏览器事件循环机制(event loop)
JS是单线程的 JS是单线程的,或者说只有一个主线程,也就是它一次只能执行一段代码.JS中其实是没有线程概念的,所谓的单线程也只是相对于多线程而言.JS的设计初衷就没有考虑这些,针对JS这种不具备并行 ...
- js事件循环机制 (Event Loop)
一.JavaScript是单线程单并发语言 什么是单线程 主程序只有一个线程,即同一时间片断内其只能执行单个任务. 为什么选择单线程? JavaScript的主要用途是与用户互动,以及操作DOM.这决 ...
- Python条件判断和循环,range()函数
条件判断经常使用if语句进行判断,表达方式为:if 条件语句: :elif:else if...用于执行第一条不满足if的判断,继续执行其它的判断.比如一个简单的if判断 Python3取消 ...
- for循环 | range 对象
# ### for循环 # 循环 遍历 迭代 # 把列表的元素一一的拿出来遍历 listvar = ["黄雄大","黄文","黄仪正",&q ...
- SSIS中循环遍历组件[Foreach Loop Container]
背景 每月给业务部门提取数据,每个分公司都要提取一般,先跑SQL,再粘贴到Excel中,然后发邮件给相关的人员.费时费力,还容易粘贴错位.因此,需要通过一个程序完成这些步骤.我首先想到的是通过SSIS ...
- 基于for循环的呼吸灯
#include "stm32f10x.h" #include "stm32f10x_gpio.h" //#include "led.h" ...
- 一道面试题引发对javascript事件循环机制(Event Loop)的 思考(这里讨论针对浏览器)
- 求两个数之间的质数 -----------基于for循环 算法思想
前端代码: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.as ...
- Java中ArrayList循环遍历并删除元素的陷阱
ava中的ArrayList循环遍历并且删除元素时经常不小心掉坑里,昨天又碰到了,感觉有必要单独写篇文章记一下. 先写个测试代码: import java.util.ArrayList; public ...
随机推荐
- ActiveMQ安装及启动
最近项目组遇到MQ的问题(项目组用的IBM MQ),突然想回来看看MQ.本来就不是太熟练,闲来无事写写看看. 不多说,首先是安转ActiveMQ,官方网址:http://activemq.apache ...
- Servlet配置
1.在Servlet中,Servlet的配置 @WebServlet(name="csvExportServlet",urlPatterns="/servlet/CsvE ...
- Servlet.service() for Servlet jsp threw exception javax.servlet.ServletException:File "/pageFoo
1.错误描述 Servlet.service() for Servlet jsp threw exception javax.servlet.ServletException:File "/ ...
- ubuntu14.04 64位 安装JDK1.7
ubuntu14.04 64位 安装JDK1.7 1 新建文件夹 youhaidong@youhaidong:~$ sudo mkdir /usr/lib/jvm 2 解压文件 youhaidong@ ...
- java中outputStream与inputStream的相互转换
package com.boco.test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; im ...
- MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 转
MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 在多线程设计中,许多人为了省事,会将对话框类或其它类的指针传给工作线程,而在工作线程中调用该类的成员函数或成员变量等等. ...
- 【BZOJ3223】文艺平衡树(Splay)
题面 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 ...
- [BZOJ1070] [SCOI2007] 修车 (费用流 & 动态加边)
Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使 ...
- java输出日志
protected final Logger logger = Logger.getLogger(User.class); logger.info("在控制台中打印的内容");
- 论文笔记(9):Multiscale Combinatorial Grouping
本文大致脉络: 对每张图片,作者首先使用 P. Doll´ar and C. Zitnick. Structured forests for fast edge detection. ICCV , 2 ...