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 ...
随机推荐
- python环境jieba分词的安装
我的python环境是Anaconda3安装的,由于项目需要用到分词,使用jieba分词库,在此总结一下安装方法. 安装说明======= 代码对 Python 2/3 均兼容 * 全自动安装:`ea ...
- 基于Jenkins+Git+Gradle的Android持续集成
本文参考了: http://my.oschina.net/uboluo/blog/157483 http://java.dzone.com/articles/automating-continuous ...
- 列出JDK中常用的Java包
列出JDK中常用的Java包 1.java.lang 2.java.sql 3.java.io 4.java.math 5.java.text 6.java.net 7.java.util 8.jav ...
- 用DataRelation给多个DataTable建立关系并显示到TreeView
DataRelation 对象执行两种功能: 它可使与正使用的记录相关的记录可用.如果在父记录 (GetChildRows) 中,则它提供子记录:如果正使用子记录 (GetParentRow),则它提 ...
- Linux系统安装软件出错
root@youhaidong-Edge-E545:/home/youhaidong# apt-get install install_flash_player_11_linux.x86_64.tar ...
- jquery Dialog弹框插件使用
var dialog = new Dialog({ title: '购物车', type: 'url', width: 520, content: "Uplolo.aspx", s ...
- 巨幅SQL优化(SQL Tuning)——秒杀十几个小时不出结果的SQL
今天接到用户的需求,某程序十几个小时没出结果了,很纳闷儿,于是让相关人员取了explain plan等信息,拿到explain plan后,搂一眼,就知道问题出在了哪里,explain plan跑偏了 ...
- Docker 小记 — MySQL 与 Redis 配置
前言 本篇随笔是继 "Docker Engine" 与 "Compose & Swarm" 之后的一个实例补充,初衷是记录测试环境中的一次 MySQL ...
- css3动画实现旋转木马
写旋转木马的时候,突发奇想想加个遮罩效果,那当然是用box-reflect属性了,然鹅,却被overflow:hidden坑了....... 写的效果就是不出来,太任性了有木有,代码无误呀,也没报错, ...
- 洛谷U19464 山村游历(Wander)(LCT,Splay)
洛谷题目传送门 LCT维护子树信息常见套路详见我的总结 闲话 题目摘自WC模拟试题(by Philipsweng),原题目名Wander,"山村游历"是自己搞出来的中文名. 数据自 ...