首页 > 其他分享 >扩容及迭代器失效问题

扩容及迭代器失效问题

时间:2023-04-12 19:44:17浏览次数:33  
标签:扩容 num 迭代 元素 插入 vector 失效

vector扩容问题

vector在尾部插入(push_back)时的扩容

void test3()
{
    vector<int> num = {1,2,3,4,5};
    cout << "num.size() = " << num.size() << endl;
    cout << "num.capacity() = " << num.capacity() << endl;
    num.push_back(6);
    cout << "num.size() = " << num.size() << endl;                                                                                           
    cout << "num.capacity() = " << num.capacity() << endl;
    num.push_back(7);
    num.push_back(8);
    num.push_back(9);
    num.push_back(0);
    num.push_back(11);
    cout << "num.size() = " << num.size() << endl;
    cout << "num.capacity() = " << num.capacity() << endl;
}

运行结果:

可以看到每次扩容都是原来容量的2倍,是固定的。

vector在任意位置插入(insert)时的扩容

void test4()
{
    vector<int> num = {1,2,3,4,5};
    num.push_back(6);
    vector<int>::iterator it = num.begin();
    it++;
    it++;
    cout << "num.size() = " << num.size() << endl;
    cout << "num.capacity() = " << num.capacity() << endl;
    cout << "设size = m = 6,capacity = n = 10,待插入元素个数为 t." << endl;
    cout << "1.当 t <= n -m 时,不会扩容" << endl;
    cout << "2.当 n - m < t < m 时,此时会按照 2 * m 进行扩容:" << endl;
    num.insert(it,5,66);
    cout << " m = num.size() = " << num.size() << endl;
    cout << " n = num.capacity() = " << num.capacity() << endl;

    cout << "3.当 n - m < t 且 m <= t 时,此时会按照 m + t 进行扩容:" << endl;
    // 注意,此时需要更新迭代器的位置才能插入
    it = num.begin();
    it++;
    it++;
    num.insert(it,11,777);
    cout << " m = num.size() = " << num.size() << endl;
    cout << " n = num.capacity() = " << num.capacity() << endl;
    
}

运行结果:

小结

对于vector的扩容,由于push_back()和insert()引起的扩容,其底层扩容机制是不一样的。

  • 在尾部插入时,每次只能插入一个元素,此时按照2 * capacity进行扩容即可,是肯定不会出现问题的。
  • 但是通过insert插入时,每次插入元素的个数不确定,所以不能单纯按照2 * capacity进行扩容。
    • 当 待插入元素t <= capacity - size 时,不需要扩容
    • 当 capacity - size < 待插入元素t ,且待插入元素t < m 时,此时按照 2 * size进行扩容
    • 当 capacity - size < 待插入元素t ,且m <= 待插入元素t 时,此时按照 size + t 进行扩容

迭代器失效问题

我们知道,vector在进行扩容时,会开辟一块新的空间,将原有空间中的内容复制过来,随后将原有的空间回收。而此时迭代器仍然指向原来的那片内存,故此时迭代器就会出现失效的问题。

 void test2()
{
    vector<int> num = {1,2,3,4,5};
    vector<int>::iterator it = num.begin();
    cout << "*it = " << *it << endl;
    printf("it = %p\n",&(*it));
    num.insert(it,3000,66);
    cout << "*it = " << *it << endl;       		 // 未定义的行为(此时it所指向的区域已经被回收了)
    printf("it = %p\n",&(*it));
    // num.insert(it,3000);                    // error,如果仍然以原有的迭代器为基准进行插入,就会出现错误

    // 解决方法:更新迭代器
    cout << "更新迭代器的位置 " << endl;
    it = num.begin();
    cout << "*it = " << *it << endl;
    printf("it = %p\n",&(*it));
    cout << "插入数据" << endl;
    num.insert(it,777);                         // ok
    cout << "*it = " << *it << endl;			// 未定义的行为
    printf("it = %p\n",&(*it));
    vector<int>::iterator it2 = num.begin();
    cout << "*it2 = " << *it2 << endl;			// ok
    printf("it2 = %p\n",&(*it2));
}

运行结果:

小结

  • 对于vector,其迭代器是真实的指针,所以可以在上述例子中用printf打印出来,但是对于其他容器的迭代器,很有可能根本不是一个真实的指针,是不可以这样操作的,这一点需要注意。
  • 由此可见,vector在插入元素时,有时会引起其底层的扩容,这时会导致原有的迭代器失效。为解决此问题,我们在插入元素之后,操作迭代器之前,最好更新一下迭代器的位置,让迭代器指向新的空间的位置,避免出错。
  • 特别的,对于deque而言,去进行insert操作的时候,也有可能迭代器失效,所以最好还是可以每次使用迭代器的时候,像vector一样,将迭代器的位置进行更新,指向新的空间的位置。

标签:扩容,num,迭代,元素,插入,vector,失效
From: https://www.cnblogs.com/MyXjil/p/17311000.html

相关文章

  • 记一次kvm虚机mysql数据库磁盘扩容操作步骤及其问题小坑
    背景:业务量持续增加,原来规划的1T磁盘空间不足以支撑业务发展存储使用,需要对数据库磁盘进行扩容。目前物理机有新增了2块3.5Tssd的数据盘用于数据库虚机磁盘扩容使用。需要安排时间对其进行操作扩容。操作思路:1、完成磁盘raid1操作,将新磁盘挂载到物理机上并添加到kvm的存储空间......
  • 行为型:迭代器模式
    定义  迭代器模式提供一种方法按顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。迭代器模式是目的性极强的模式,它主要是用来解决遍历问题。es6中的迭代器  JS原生的集合类型数据结构,有Array(数组)和Object(对象),在ES6中,又新增了Map和Set。四种数据结构各自有......
  • 虚拟机磁盘的扩容 ,及扩展磁盘也扩展分区。
    问题: 我在虚拟机中编译android12,200G的硬盘用完了,在95%的时候时候,提示空间不足,然后报错。已经编译了3个小时了,如果是别的情况,我就直接添加一块新的硬盘了,但是现在只能看看能不能直接扩容这个分区,毕竟我不行在从新编译一遍。 过程: 1 首先在虚拟机中把之前的硬盘......
  • Java中常用算法及示例-分治、迭代、递归、递推、动态规划、回溯、穷举、贪心
    场景1、分治算法的基本思想是将一个计算复杂的问题分成规模较小、计算简单的小问题求解,然后综合各个小问题,得到最终答案。2、穷举(又称枚举)算法的基本思想是从所有可能的情况中搜索正确的答案。3、迭代法(IterativeMethod)无法使用公式一次求解,而需要使用重复结构(即循环)......
  • linux 给lvm磁盘扩容
    目录linux给lvm磁盘扩容扩容步骤确认可用空间创建新的物理卷将物理卷添加到现有的卷组中扩展逻辑卷linux给lvm磁盘扩容早上到公司发现磁盘满了,挂载点是一个lvm跟领导确认后决定先扩容再清理,原先是1T,决定扩容到2TLVM(逻辑卷管理)是一种用于Linux系统的磁盘分区技术,它允许管理......
  • XFS 文件系统扩容
    当磁盘做成XFS文件系统格式后我们怎么扩容。  场景1:整块磁盘直接格式做成XFS文件系统挂载到指定目录后,扩容场景。 无截图举例描述1.举例现在我们有100G的磁盘。我们mkfs.xfs /dev/sdb 后,将磁盘挂载/data 目录下(mount /dev/sdb /mount)。后续因为磁盘......
  • LVM扩容操作-Centos7(对根扩容)
    之前也写过一篇文件系统扩容的文章,这次为了加深印象,再记录一遍,只记录操作流程。前文:https://www.cnblogs.com/sxFu/p/13426362.html一、环境根目录50G,现需要对根目录再扩100G因为是新申请的机器,没有业务,随便造,但是若是生产环境的机器,建议挂载其它数据目录 二、分区新磁盘f......
  • Go 语言切片是如何扩容的?
    原文链接:Go语言切片是如何扩容的?在Go语言中,有一个很常用的数据结构,那就是切片(Slice)。切片是一个拥有相同类型元素的可变长度的序列,它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。切片是一种引用类型,它有三个属性:指针,长度和容量。底层源码定义如下:typeslicest......
  • Go 语言切片是如何扩容的?
    原文链接:Go语言切片是如何扩容的?在Go语言中,有一个很常用的数据结构,那就是切片(Slice)。切片是一个拥有相同类型元素的可变长度的序列,它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。切片是一种引用类型,它有三个属性:指针,长度和容量。底层源码定义如下:typeslices......
  • Rust编程语言入门之函数式语言特性:-迭代器和闭包
    函数式语言特性:-迭代器和闭包本章内容闭包(closures)迭代器(iterators)优化改善12章的实例项目讨论闭包和迭代器的运行时性能一、闭包(1)-使用闭包创建抽象行为什么是闭包(closure)闭包:可以捕获其所在环境的匿名函数。闭包:是匿名函数保存为变量、作为参数可在一个地方......