C++11基于范围的for循环&vector容器扩容详解&迭代器失效

1 篇文章 0 订阅

目录

C++11基于范围for循环

vector容器扩容详解

迭代器失效

总结


C++11基于范围for循环

对于一个有范围的集合
来说在程序代码中指定循环的范围有时候是多余的,还可能犯错误。

为此C++11中引入了基于范围的for循环。

语法:

语法:

for (迭代的变量 : 迭代的范围)

{

        // 循环体。

}

对于一个vector<int>容器,我们一般会这样遍历:

 

但是基于范围的for循环是这样的:

 

它的原理就是从v01中取到一个内容赋值给变量val;然后在循环体中操作val;

记住他的原理,很重要!

迭代的范围不仅仅可以使用容器,也可以使用数组或者初始化列表,如下:

还有很重要的一点:

如果容器中的元素是结构体和类,迭代器变量应该申明为引用,加const约束表示只读。

我们演示一下类,代码如下:

#include <iostream>
#include <vector>
using namespace std;

class MyClass
{
public:
	string m_name;

	MyClass() { cout << "默认构造函数AA()。\n"; }

	MyClass(const string& name) : m_name(name) { cout << "构造函数,name=" << m_name << "。\n"; }

	MyClass(const MyClass& a) : m_name(a.m_name) { cout << "拷贝构造函数,name=" << m_name << "。\n"; }

	MyClass& operator=(const MyClass& a) { m_name = a.m_name;  cout << "赋值函数,name=" << m_name << "。\n";  return *this; }

	~MyClass() { cout << "析构函数,name=" << m_name<<"。\n"; }
};

int main()
{

    return 0;
}

首先我们需要先明白vector容器扩充的原理:

main函数代码如下:

int main()
{
    vector<MyClass> v;
	cout << "v.capacity()=" << v.capacity() << "----------------------------------\n";
	v.emplace_back("www");
	cout << "v.capacity()=" << v.capacity() << "----------------------------------\n";
	v.emplace_back("eee");
	cout << "v.capacity()=" << v.capacity() << "----------------------------------\n";
	v.emplace_back("rrr");
	cout << "v.capacity()=" << v.capacity() << "----------------------------------\n";

    return 0;
}

capacity指的是vector已使用的大小;

运行:

接下来我们一步一步分析;

vector容器扩容详解

首先我们使用了vector的无参构造,它已使用容量大小是0;

然后emplace_back()函数构造了一个m_name为"www"的对象,调用了一次MyClass的构造函数;

然后显示容量此时为1;

重点:

然后再次构造一个m_name为eee的对象,输出了三行日志,为什么呢?

首先vector容器原来的大小capacity()=1,1是无法满足两个元素的("www","eee"),所以需要扩充,但是vector容器的扩充分为四个步骤

1、申请一个新的地址,这个地址可以存放下"www",和"eee"的对象,这一步没有日志;

2、构造"eee"存放到新的地址,显示构造函数,name="eee"。

3、将原来地址中的"www"拷贝过来,显示拷贝构造,name="www"。

4、释放原来的内存,显示析构函数,name="www"。

知道了vector扩充的步骤,下面的日志大家可以自己分析了;

讲vector容器扩充的步骤,主要是为了分析日志,让大家不要对接下来的输出的日志感觉迷茫;

现在我们已知,该容器有三个元素,那么我们会怎么访问这三个元素呢?

我们首先想到的肯定是这样写:

for (auto a : v)
	cout << a.m_name << " ";
cout << endl;

auto可以自动推导出v的类型;

运行一下:

红框上面的我们已经介绍了,不会再迷茫了;

红框中的内容又输出了一堆,为什么呢?我们分析一下:

还记不记得上面讲的基于范围for循环原理?从迭代的范围中取到一个内容赋值给迭代的变量 

在这里就是从v中取到一个内容赋值给a;

v中都是什么内容呢?"www"、"eee"、"rrr"的对象,一共三个对象;

那么第一次循环,我们将"www"的对象赋值给a,会调用拷贝构造,显示拷贝构造函数,name=www。

然后我们循环体中显示www和一个空格(也就是第二行日志的开头)

然后因为我们是将"www"的对象拷贝给a,那么a使用完这个对象肯定要析构啊,所以调用了析构函数,显示析构函数,name=www。

至此前两行的日志已经解释完毕;下面的原理都一样,不解释了;

所以下面这张图我们应该明白了:

for循环输出一个内容,就要拷贝一次,析构一次,这肯定不是我们想要的结果对吧?

那么怎么办呢?

因为我们a是拷贝过来的副本,所以a才有必要析构它,但是如果a使用的就是vector容器中的对象,不是拷贝过来的副本,就没有必要析构它,因为他不归a所管; 

怎么让a是容器中的对象而不是拷贝的副本呢?引用;

如下:

没有拷贝构造,因为我们使用的是vector容器中的对象本身,不会发生拷贝,既然没拷贝就不会有拷贝构造的日志,a也不会析构它;

这样还不是最完美的,为什么呢?

因为我们的a现在拿到的不是拷贝副本,而是vector容器中真正的数据,那么如果a操作了vector容器中的数据还是比较危险的,所以a应该加上const,不可修改vector容器中的数据; 

如下:

所以这就是为什么:如果容器中的元素是结构体和类,迭代器变量应该申明为引用,加const约束表示只读。

迭代器失效

最后基于范围的for循环还要注意迭代器失效的问题,在vector容器中,使用resize()、reserve()、assign()、push_back()、pop_back()insert()、erase()等函数会引起vector容器的动态数组发生变化,可能导致vector迭代器失效

演示一下,迭代器失效,代码如下:

 

上面是正常情况下的运行结果,下面取消注释:

 

只有1,正常显示了,这是为什么呢?就是迭代器失效了

如果不知道为什么迭代器失效,说明上面的vector容器扩充的四个步骤没有学好 

我们分析一下:

首先第一次循环,先输出val和空格;此时val是第一个元素,所以输出了1;

然后v.push_back(2);这一行代码,由于原来的vector容器的数量不能满足再填充一个2进去,所以这行代码会触发vector容器的扩充;

扩充四个步骤:1、申请新地址;2、将新的内容填充到新地址;3、将原地址中的内容拷贝到新地址;4、释放原地址

当我们的v.push_back(2)运行完了之后,vector容器元素的地址已经发生了改变,但是我们的val还是在原来的地址中取内容,因为这是已经编译好的地址,同时原来的地址已经被释放了,所以输出了四个垃圾值;

最后注意:

如果我们想验证容器扩充之后,地址发生了改变,那么假设我们的容器是vector<int> aa({1,2,3,4,5});那么我们想要输出aa元素的首地址,千万不能这样写:&aa;因为这样获得的是vector容器的地址,在栈区;我们想要输出aa元素的首地址必须使用STL提供的函数,aa.data()这样就能获取aa元素的首地址;

总结

1、迭代的范围可以是数组名、容器名、初始化列表或者可迭代的对象(支持begin()、end()、++、==)。

2、数组名传入函数后,已退化成指针,不能作为容器名。

3、注意迭代器失效的问题。

4、输出vector元素的地址,必须使用vector中的data()函数,不能取地址vector对象;

本篇文章内容至此结束!感谢观看!

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值