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 ...
随机推荐
- discuz 更换域名 导致qq登录不能用的问题
今天论坛换了域名,导致qq登录不能用.于是各种百度,终于找到了解决方案,特此记录一下 解决方法:1,首先清空你站点的id和key,并且设置为未注册云平台: 2,找一个新域名(未开过云平台的就可),如果 ...
- dojo中引入FusionCharts柱状图报错
1.今天,做项目的过程中,我发现Java后台查询的数据都是正确的,并且拼接成JSON格式也正确,但是传到JSP页面时,图无法显示出来还报错,后来经过检查发现是JavaScript和引入FusionCh ...
- 用Dw CS6运行静态页面出问题
1.报错截图一 2.报错截图二
- Nginx负载均衡和反向代理的配置和优化
负载均衡 负载均衡是由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位, 反向代理 是指以代理服务器来接受internet上的请求,然后将请求转给内部的服务器 常见的负载均衡 1. ...
- Invalid property 'driver_class' of bean class
1.错误描述 INFO:2015-05-01 13:06:07[localhost-startStop-1] - Initializing c3p0-0.9.2.1 [built 20-March-2 ...
- JVM内存分析
1.java内存模型分析 java虚拟机运行时数据存储区域包括线程隔离和线程共享两类,整个PC的内存图如下所示: 下面对以上内存区域说明: 1.1 register和cache 当代计算机一般有多个c ...
- 关于ThinkCMF自带插件上传不了图片的解决方法
第一个原因:第一次安装的时候提示file_upload未打开,所以当上传的时候回上传失败 解决方法:在php.ini里打开file_uploads = On 第二个原因:一开始还可以上传,但是当删除了 ...
- js弹窗登录效果(源码)--web前端
1.JS弹窗登录效果 <!DOCTYPE html><html lang="en"><head> <meta charset=" ...
- JS实现回到Top(顶部)--JavaScript
当我们浏览一段很长的网页时,已经看到底部了,想回到顶部看前面的内容,可是需要滚动好几转鼠标滑轮或者拉动滚动条走好长“一段路”.这对于用户来说,体验效果是不够好的.如果我们借助简单的一个按钮,点击一下就 ...
- 【CF932G】Palindrome Partition(回文树,动态规划)
[CF932G]Palindrome Partition(回文树,动态规划) 题面 CF 翻译: 给定一个串,把串分为偶数段 假设分为了\(s1,s2,s3....sk\) 求,满足\(s_1=s_k ...