9-3 顺序容器操作

9.3.1 向顺序容器添加元素

image-20220222145643748

push_back#

  • 除了array和forward_list以外,都可以使用push_back
  • 插入的是对象的值的拷贝,而不是对象本身
string s;
s.push_back('a'); //等价于 s+='a'

push_front#

  • list,forawrd_list和deque支持
list<int> ilist;
for(int i = 0; i != 4; ++i)
    ilist.push_front(i);

在容器的特定位置添加元素#

slist.insert(iter, "hello");:将“hello”添加到iter之前的位置

虽然某些容器不支持push_front 操作,但它们对于 insert 操作并无类似的限制(插入开始位置)。因此我们可以将元素插入到容器的开始位置,而不必担心容器是否支持push_front:

vector<string> svec;
list<string> slist;

//等价于调用slist.push_front("hello")
slist.insert(slist.begin(), "hello");

//vector不支持push_front,但我们可以插入到begin()之前
//警告:在vector结尾之外的地方插入元素可能会很慢
svec.insert(svec.begin(), "hello");

插入范围内元素#

//在svec结尾前添加10个元素,初始化为"Anna"
svec.insert(svec.end(), 10, "Anna");

//将svec的最后两个元素添加到slist的开始位置
slist.insert(slist.begin(), svec.end()-2, svec.end());
slist.insert(slist.begin(), {"aaa","bbb","ccc"});

注意

  1. 传递给insert的迭代器不能指向添加元素的目标容器
  2. insert返回第一个新加入元素的迭代器

使用insert的返回值#

理解下述循环:等价于不断调用push_back

list<string> lst;
auto iter = lst.begin();
while(cin >> word)
    iter = lst.insert(iter, word);

使用emplace操作#

新成员 对应的成员
emplace_front push_front
emplace insert
emplace_back push_back
  • push_front、insert、push_back:

​ 接受对象,然后将对象的拷贝压入容器

  • emplace_front、emplace、emplace_back

    接受参数,将参数传给对象的构造函数,直接构造对象压入容器

image-20220222152720092

emplace的参数与对象的构造函数有关且必须相匹配

image-20220222152837942

9.3.2 访问元素

image-20220222153019432

也可以用迭代器begin()end()

访问成员函数返回的是引用#

if(!c.empty()){
    c.front() = 42; 		//第一个元素变为42
    auto &v = c.back(); 	//获得指向最后一个元素的引用
    v = 1024; 				//改变最后一个元素
    auto v2 = c.back(); 	//不是引用,而是c.back()的拷贝
    v2 = 0; 				//未改变c的值
}

下标操作和安全的随机访问#

提供随机访问容器【vector,string,deque,array】都提供下标操作

vector<string> svec;	//空svec
cout<<svec[0];			//运行时错误
cout<<svec.at(0);		//抛出out_of_range异常

9.3.3 删除元素

image-20220222153716985

删除元素的成员函数并不检查其参数,所以在删除元素之前,程序员必须确保他们是存在的

pop_front和pop_back成员函数#

  • vector、string不支持pop_front
  • forward_list不支持pop_back

注意点

  1. 确保非空才能弹出
  2. 操作返回void,需要值的话必须在弹出之前保存
while(!ilist.empty()){
    process(ilist.front());	//对ilist的首元素进行一些操作
    ilist.pop_front();		//完成处理后删除首元素
}

从容器内部删除一个元素#

erase(iter):删除迭代器iter指向的元素;返回删除的最后一个元素的之后一个元素

【insert返回的是插入的元素】

//删除list中所有的奇数元素
list<int> lst = {0,1,2,3,4,5,6,7,8,9};
auto it = lst.begin();
while(it != lst.end()){
    if(*it % 2) 				//若元素为奇数
        it = lst.erase(it);		//删除此元素,it步进
    else
        ++it;
}

删除多个元素#

erase(iter1, iter2):删除iter1和iter2所指向的范围【左闭右开】中的元素;返回删除的最后一个元素的后一个元素

slist.clear(); //清空元素
slist.erase(slist.begin(), slist.end());  //等价于上式

9.3.4 特殊的forward_list操作:略

9.3.5 改变容器的大小

成员函数 说明
c.resize(n) n<c.size(),则删除多余元素;n>c.size(),则添加元素,进行值初始化,若为类类型则调用默认构造函数【没有则报错】
c.resize(n,t) 调整c的大小到n,并初始化添加的元素为t
list<int> ilist(10,42); 	//10个int:每个值都是42
ilist.resize(15);			//添加5个为0的int到ilist末尾
ilist.resize(25, -1);		//将10个-1的int添加到ilist末尾
ilist.resize(5);			//从ilist末尾删除20个元素

9.3.6 容器操作可能使迭代器失效

添加元素时#

  • 如果容器是vector或string
    • 存储空间被重新分配,则指向容器的迭代器、指针和引用都会失效。
    • 存储空间未重新分配,指向插入位置之前的元素的迭代器、指针和引用仍有效,但指向插入位置之后元素的迭代器、指针和引用将会失效。
  • 对于deque
    • 插入到除首尾位置之外的任何位置都会导致迭代器、指针和引用失效。
    • 如果在首尾位置添加元素,迭代器会失效,但指向存在的元素的引用和指针不会失效。
  • 对于list 和 forward_list,指向容器的迭代器(包括尾后迭代器和首前迭代器)、指针和引用仍有效。

删除元素时#

  • 对于list和 forward_list,指向容器其他位置的迭代器(包括尾后迭代器和首前迭代器)、引用和指针仍有效。
  • 对于deque,如果在首尾之外的任何位置删除元素,那么指向被删除元素外其他元素的迭代器、引用或指针也会失效。如果是删除 deque的尾元素,则尾后迭代器也会失效,但其他迭代器、引用和指针不受影响;如果是删除首元素,这些也不会受影响。
  • 对于vector和 string,指向被删元素之前元素的迭代器、引用和指针仍有效。
  • 注意:当我们删除元素时,尾后迭代器总是会失效。

使用失效的迭代器、指针或引用是严重的运行时错误。

建议:管理迭代器
当你使用迭代器(或指向容器元素的引用或指针)时,最小化要求迭代器必须保持有的程序片段是一个好的方法。
由于向迭代器添加元素和从迭代器删除元素的代码可能会使迭代器失效,因此必保证每次改变容器的操作之后都正确地重新定位迭代器。这个建议对vector,strin和deque尤为重要。

编写改变容器的循环程序#

添加/删除vector、string或deque元素的循环程序必须考虑迭代器、引用和指针可能失效的问题。程序必须保证每个循环步中都更新迭代器、引用或指针。如果循环中调用的是 insert或erase,那么更新迭代器很容易。这些操作都返回迭代器,我们可以用来更新:

//傻瓜循环,删除偶元素,复制每个奇元素
vector<int> vi = {0,1,2,3,4,5,6,7,8,9};
auto iter = vi.begin();
while(iter != vi.end()){
    if(*iter % 2){
        iter = vi.insert(iter, *iter);  //复制奇元素
        iter+=2;//向前移动迭代器,跳过当前元素以及插入到它之前的元素
    }
    else
        iter = vi.erase(iter); //删除偶元素
    	//不必向前移动迭代器,iter已经指向删除元素的后一个元素
}

不要保存end返回的迭代器#

当我们添加/删除vector或string 的元素后,或在 deque 中首元素之外任何位置添加/删除元素后,原来end返回的迭代器总是会失效。

因此,添加或删除元素的循环程序必须反复调用end,而不能在循环之前保存end返回的迭代器,一直当作容器末尾使用。

通常C++标准库的实现中 end ()操作都很快,部分就是因为这个原因。

例子:考虑这样一个循环,它处理容器中的每个元素,在其后添加一个新元素。我们希望循环能跳过新添加的元素,只处理原有元素。

image-20220222185425829

image-20220222185434256

posted @   咪啪魔女  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示