c++11学习笔记(9)
(1)向容器中添加元素和从容器中删除元素的操作可能会使指向容器元素的指针、引用或者迭代器失效。一个失效的指针引用或迭代器将不再表示任何元素。使用失效的指针引用迭代器是一种严重的程序设计错误,可能会引起和使用未初始化指针一样问题。
(2)在向容器添加元素后,如果容器是vector或string,且存储空间被重新分配,则指向容器的迭代器指针引用都会失效。如果存储空间没有重新分配,指向插入位置之前的元素的迭代器指针引用仍然有效,但指向插入位置之后的全部失效。
(3)对于deque队列,插入到除首尾之外的任何位置都会导致迭代器指针引用失效。如果在首尾位置添加元素,迭代器会失效,但指向存在的元素的引用和指针不会失效。
(4)对于list和forward_list,指向容器的迭代器(包括尾后迭代器和首前迭代器)、指针和引用仍然有效。
(5)对于list和forward_list,指向容器其他位置的迭代器(包括尾后迭代器和首前迭代器)引用和指针仍然有效。
(6)对于deque,如果在首尾之外的任何位置删除元素,那么指向被删除元素之外的其他元素的迭代器引用指针也会失效。如果是删除deque的尾元素,则尾后迭代器也会失效。但其他迭代器引用和指针不受影响。如果是删除首元素,这些也不受影响。
(7)对于vector和string,指向被删元素之前元素的迭代器引用和指针仍然有效。
(8)添加删除vector、string、deque元素的循环程序必须考虑迭代器引用和指针可能失效的问题。程序必须保证每个循环步骤中都更新迭代器引用指针。如果循环中调用是insert或erase,那么更新迭代器很容易,因为这些操作返回迭代器,我们可用来更新。
(9)傻瓜循环,删除偶数元素,复制每个奇数元素。
vector<int> vi={0,1,2,3,4,5,6,7,8,9};
auto iter=vi. begin();
//调用的是begin而不是cbegin,因为我们要改变v1
while(iter!=vi.end())
{
if(*iter % 2)
{
iter=vi. insert(iter, *iter);//复制当前元素
iter+=2;//向前移动迭代器,跳过当前元素以及插入到它之前的元素
}
else
{
iter=vi. erase(iter);//删除偶数元素
//不应向前移动迭代器,iter指向我们删除的元素以后的元素
}
}
这个程序删除vector中的偶数值元素,并赋值每个奇数值元素。我们在调用insert和erase以后都要更新迭代器,因为两者都会使迭代器失效。
(10)在调用erase之后,不必递增迭代器,因为erase返回的迭代器已经指向序列中下一个元素。调用insert之后,需要递增迭代器两次,记住insert在给定位置之前插入新元素,然后返回指向新插入元素的迭代器。因此,在调用insert以后,iter指向新插入元素,位于我们正在处理的元素之前。我们将迭代器递增两次,恰好越过了新添加的元素和正在处理的元素,指向下一个未处理的元素。
(11)为了支持快速随机访问,vector将元素连续存储,每个元素紧挨着前一个元素存储。通常情况下,我们不必关心一个标准库类型是如何实现的,而只需关心它如何使用。然而对于vector和string,其部分实现渗透到了接口中。
(12)假定容器中元素是连续存储的,且容器的大小是可变的,考虑向vector或string中添加元素会发生什么。如果没有空间容纳新元素,容器不可能简单的将它添加到内存中的其他位置,因为元素必须连续存储。容器必须分配新的内存空间来保存已有元素和新元素,将已有元素从旧位置移动到新空间中,然后添加新元素,释放旧存储空间。如果我们每添加一个元素,vector就执行一次这样得内存分配和释放操作,性能会慢到不可接受。
(13)为了避免这种代价,标准库实现者采用了可以减少容器空间重新分配次数的策略。当不得不获取新的内存空间时,vector和string的实现通常会分配比新的空间需求恒大的内存空间,容器预留这些空间作为备用,可以用来保存更多的新元素。这样,就不用再次添加新元素都重新分配容器的内存空间了。
(14)vector和string类提供了一些成员函数,允许我们和它的实现中内存分配部分互动。capacity操作告诉我们容器在不扩张内存空间的情况下可以容纳多少个元素。reverse操作允许我们通知容器它应该准备保存多个元素。
(15)理解capacity和size的区别非常重要,容器的size是指它已经保存的元素数目,而capacity则是在不分配新的内存空间的前提下它最多可以保存多少个元素。
(16)c. shrink_to_fit()
请将capacity减少为与size相同的大小
(17)c. capacity()
不重新分配内存空间的话,c可以保存多少元素
(18)c. reverse(n)分配至少能容纳n个元素的内存空间
(19)除了顺序容器共同的操作之外,string类型还提供了一些额外的操作,这些操作中的大部分要么是提供string类和c风格字符数组之间的相互转换,要么是增加了允许我们用下标替代迭代器的版本。
(20)初始化string对象的方式
string s1
string s2(s1)
string s2=s1
string s3("value")
string s3="value"
string s4(n, "c")
还有下面三种构造函数
string s(cp, n) s是cp指向的数组中前n个字符的拷贝,此数组应该至少包含n个字符
string s(s2, pos) s是string s2从下标pos开始的字符的拷贝
string s(s2, pos, len) s是从下标pos开始len个字符的拷贝
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下