9.1 顺序容器概述
确定使用哪种顺序容器
通常使用vector是最好的选择
9.2 容器库概述(本节所有容器均适用)
对容器可以保存的元素类型的限制
- 有些类没有提供默认构造函数,我们可以定义一个这种类型对象的容器,但我们在构造这种容器时不能只传递给它一个数目参数.
9.2.1 迭代器
- 与容器一样,迭代器有着公共的接口:如果一个迭代器提供某个操作,那么所有提供相同操作的迭代器对这个操作的实现方式都是相同的.但是有一个例外:forward_list迭代器不支持递减运算符
迭代器范围
- begin和end first和last(指向尾元素之后的元素)
使用左闭合范围蕴含的编程假定
9.2.2 容器类型成员
- 为了索引int的vector中的元素使用迭代器:
vector<int>::iterator
- 为了读取string的list中的元素:应该使用的类型是:
list<string>::value_type
- 为了写入数据,应该使用的类型是;
list<string>::reference
9.2.3 begin和end成员
- 不以c开头的函数都是被重载过的.也就是说,实际上有两个名为begin的成员,一个是const成员返回const_iterator,另外一个返回的是非常量成员iterator.当我们对一个非常量对象调用这些成元返回普通的.
9.2.4 容器定义和初始化
将一个容器初始化为另外一个容器的拷贝
- 将一个新容器创建为另外一个容器的拷贝的方法有两种:
- 可以直接拷贝整个容器(除array)
- 拷贝有一个迭代器对指定的范围
list<string> ls = {"1","2","3"};
vector<const char *> vs = {"4","5","6"};
list<string> list2(vs.begin(),vs.end());
列表初始化
与顺序容器大小相关的构造函数
标准库array具有固定大小
9.2.5 赋值和swap
使用assign(仅顺序容器)
9.2.6 容器大小操作
9.2.7 关系运算符
- 除了无序关联容器外的所有容器都支持关系运算符.关系运算符左右两边的运算对象必须是相同的容器,且必须保存相同类型的元素.
- 运算符的工作方式与string的关系运算类似.
容器的关系运算符使用元素的关系运算符完成比较
只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器
9.3 顺序容器操作(顺序容器特有操作)
9.3.1 向顺序容器添加元素
- 在一个vector或string的尾部之外的任何位置,或是deque的首尾之外的任何位置添加元素都需要移动元素
使用push_back(除array和forward_list之外)
使用push_front
- list,forward_list,deque支持
- deque和vector提供随机访问元素的能力
在容器的特定位置添加元素(insert)
- insert(iterator,element)
插入范围内元素
- 传递的迭代器不能指向添加新元素的目标容器
- 新标准之下,接受元素个数或范围的insert版本返回指向第一个新加入元素的迭代器
使用insert返回值
使用emplace操作
向一个vector,string,deque插入元素会使现有指向容器的迭代器,引用,指针生效.这时就应该结合insert返回的元素来进行操作
9.3.2 访问元素
访问成员函数返回的是引用
小标操作和安全的随机访问
- 提供下标运算符的容器(string,vector,deque和arrate都提供下标运算符).
- 如果我们希望确保下标是合法的,可以使用at成员函数.如果at下标越界,at会抛出一个out_of_range异常
9.3.3 删除元素
pop_front和pop_back成员函数
- 与vector和string不支持push_front一样,这些类型也不支持pop_front
- 不能对一个空容器执行1弹出操作
while(!ilist.empty()){
}
从容器内部删除一个元素(erase)
删除多个元素
slist.clear()
slist.erase(slist.begin(),slist.end())
int main()
{
int a[] = {0,1,1,2,3,5,8,13,21,55,89};
vector<int> vi ;
list<int> li;
vi.assign(a,a+11); // 将数组上的元素拷贝到vector中
li.assign(a,a+11);
vector<int>::iterator vit = vi.begin();
while (vit != vi.end()){
if(*vit %2 == 0){
vit = vi.erase(vit);
} else{
vit++;
}
}
vit = vi.begin();
while (vit != vi.end()){
cout<<*vit<<ends;
vit++;
}
return 0;
}
9.3.4 特殊的forward_list操作
- 由于forwar_list是一个单向列表,删除或者添加元素需要这个元素的前驱才能操作,但是在一个单链表中没有简单的方法获得一个元素的前驱节点,所以删除和添加元素操作都是通过改变给定元素之后的元素来完成的。
- 由于这些操作与其他容器上的操作不同,forward_list并未定义insert,emplace,erase而是定义了insert_after,emplace_after,erase_after的操作。
- 当在forward_list中添加或删除元素时,我们必须关注两个迭代器,一个指向我们要处理的元素,另一个执行其前驱。
int main() {
int a[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89};
forward_list<int> fi;
fi.assign(a, a + 11); // 将数组上的元素拷贝到forward_list中
int x1 = 21, x2 = 2; //查找x1并将x2插入到紧挨x1之后的位置
forward_list<int>::iterator it = fi.before_begin();//返回第一个元素前面的迭代器
forward_list<int>::iterator nestIt = fi.begin();//返回首元素
it = fi.begin();
while (it != fi.end()) {
cout << *it << ends;
it++;
}
it = fi.before_begin();
cout<<endl;
//查找x1
while (nestIt != fi.end()) {
if (*nestIt == x1) {
fi.erase_after(it); //删除it之后的元素,it在nextIt的前面
break;
} else {
it++;
nestIt++;
}
}
it = fi.begin();
while (it != fi.end()) {
cout << *it << ends;
it++;
}
nestIt = fi.begin();
//插入x1到x2的位置
while (nestIt != fi.end()) {
if (*nestIt == x2) {
fi.insert_after(nestIt, x1);
break;
} else {
nestIt++;
}
}
cout << endl;
it = fi.begin();
//插入x1到x2的位置
while (it != fi.end()) {
cout << *it << ends;
it++;
}
/*
* 0 1 1 2 3 5 8 13 21 55 89
0 1 1 2 3 5 8 13 55 89
0 1 1 2 21 3 5 8 13 55 89
*/
return 0;
}
9.3.5 改变容器大小(resize)
- 对于元素是类类型,则单参数resize版本要求改类型必须提供一个默认构造函数。
9.3.6 容器操作可能是迭代器失效
- 向容器中添加元素和从容器中删除元素的操作可能会使指向元素的指针,引用或者迭代器失效。而一个失效的指针,引用或迭代器将不再表示任何元素
- 容器是vector或者string的情况下,在向容器添加元素后,如果
容器扩容
,即容器的存储空间重新分配,则指向容器的迭代器失效
- 容器是vector或者string的情况下,在向容器添加元素后,如果容器
未扩容
,则指向插入位置之前
的元素的迭代器有效
,但指向插入位置之后
元素的迭代器将会失效
,这是因为插入位置后的元素次序发生变化使得原本指向某元素的迭代器不再指向希望指向的元素。 - 容器是deque的情况下,插入到任何位置都会导致迭代器。
- 容器是list和forward_list的情况下,插入到任何位置,指向容器的迭代器仍然有效,
编写改变容器的循环程序
- insert在给定位置之
前
插入新元素,然后返回指向新元素的迭代器,所以必须加两次,越过插入的新元素和正在处理的元素
不要保存end返回的迭代器
如果在一个循环中插入,删除deque或vector中的元素,不要缓存end返回的迭代器’
第一题:
- list和forward_list与其他容器的一个不同是,迭代器不支持加减运算,因为链表中的元素并非在内存中联系存储,因此无法通过地址的加减在元素间远距离移动。因此,应多次调用++来实现与迭代器加法相同的效果。
list和forward_list支持
++不支持
+=`
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;
cout<<1<<endl;
}
else
{
iter = vi.erase(iter); //删除之后返回我们删除的元素之后的元素
}
}
int main() {
list<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++;
iter++;
}
else
{
iter = vi.erase(iter); //删除之后返回我们删除的元素之后的元素
}
}
iter = vi.begin();
while (iter != vi.end()){
cout<<*iter<<ends;
iter++;
}
return 0;
}
/删除奇数,复制偶数
int main() {
//对于forward_list删除元素时,由于是单链表结构,需要维护前驱和后继两个迭代器
forward_list<int> vi = {0,1,2,3,4,5,6,7,8,9};
auto iter = vi.begin();
auto prev = vi.before_begin();
while(iter != vi.end())
{
if(*iter % 2) //奇数
{
iter = vi.insert_after(iter,*iter); //返回插入的元素
prev = iter;
iter++;
}
else
{
iter = vi.erase_after(prev); //删除之后返回我们删除的元素之后的元素
}
}
iter = vi.begin();
while (iter != vi.end()){
cout<<*iter<<ends;
iter++;
}
return 0;
}
第二题
- 编译器处理形参的顺序可能是不同的,由右向左或者有左向右都有可能。
9.4 vector对象是如何增长的
管理容量的成员函数
capacity和size
- capacity是在不分配内存空间的前提下它最多可以保存多少元素,size是指它已经保存的元素的数目。
- 实际上只要没有操作需求超过vector的容量,vector就不能重新分配内存空间
9.5 额外的string操作
9.5.1 构造string的其他方法
substr操作
习题
9.5.2 改变string的其他方法
append和replace函数
改变string的多种重载函数
void test(string &s, string& oldVal, string& newVal)
{
if (!s.size())
return; //s为空
string::iterator its = s.begin();
int j = 0; //记录正在遍历的s的位置
while (its != s.end()) {
if (*its == oldVal[0] && j < s.size())
{
cout << "找到首元素" << endl;
string::iterator oldIt = oldVal.begin();
while (oldIt !=oldVal.end() && *its == *oldIt && j< s.size())
{
oldIt++;
its++;
}
if (oldIt == oldVal.end())
{ //s中有oldval并找到其位置
cout << "找到了" << endl;
s.erase(j,oldVal.size());
s.insert(j, newVal);
break;
}
}
else
{
its++;
j++;
}
}
}
int main(int argc, char** argv)
{
string s = "newtho";
string old = "tho";
string news = "news";
test(s, old, news);
cout << s << endl; //newsnew
return 0;
}
9.5.3 string搜索操作
指定在哪里开始搜索
在字符串中循环的搜索字符串出现的所有位置
void test(string &s, string& findStr)
{
string::size_type pos = 0;
while ((pos = s.find_first_of(findStr,pos)) != string::npos)
{
cout << "pos = " << pos<<"Element is " << s[pos]<< endl;
++pos;
}
}
s.find_first_of(str)
查找的是str中的任意一个字符在s首次出现的位置
逆向搜索
rfind
习题
for-each语句遍历string会乱码
string::size_type pos = 0;
string str = "qwertyuiopasdfghjklzxcvbnm";
string::size_type len = str.size();
for(int i =0;i< len;i++)
{
str += toupper(str[i]);
cout << str[i] << " " << toupper(str[i]) << " " << str << endl;
}
cout << str << endl;
9.5.4 compare函数
9.5.5 数值转换
9.5 容器适配器
定义一个适配器
栈适配器
队列适配器