顺序容器2

Posted ygeloutingyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了顺序容器2相关的知识,希望对你有一定的参考价值。

顺序容器类型:

vector    可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢

deque    双端队列。支持快速随机访问。在头尾位置插入/删除速度很快

list       双向列表。只支持双向顺序访问。在 list 中任何位置进行插入/删除操作速度都很快

forward_list   单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作都很快

array      固定大小数组。支持快速随机访问。不能添加或删除元素

string     与 vector 相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度很快

 

容器操作(后面再提具体每个容器操作对元素的其他限制):

类型别名

iterator      此容器的迭代器类型

const_iterator      可以读取元素,但不能修改元素的迭代器类型

size_type       无符号整数类型,足够保存此种容器类型最大可能容器大小

difference_type   带符号整数类型,足够保存两个迭代器之间的距离

value_type     元素类型

reference       元素的左值类型,与 value_type& 含义相同

const_reference  元素的 const 左值类型,即 const value_type&

构造函数

C c;        默认构造函数,构造空容器

C c1(c2);     构造 c2 的拷贝 c1

C c(b, e);     构造 c,将迭代器 b 到 e 指定的范围内的元素拷贝到 c(array 不支持)

C c{a, b, c, d...}   列表初始化 c

赋值与 swap

c1 = c2;       将 c1 中的元素替换为 c2 中的元素

c1 = {a, b, c, d...}   将 c1 中的元素替换为列表中元素(不适用于 array)

a.swap(b);      交换 a 和 b 的元素

swap(a, b);     等价于 a.swap(b)

大小

c.size();       c 中元素的数目(不支持 forward_list)

c.max_size();   c 中可保存的最大数目的元素数目

c.empty();      若 c 中存储了元素,返回 false,否则返回 true

添加/删除元素(不适用于 array)

注:在不同容器中,这些操作的接口都不同

c.insert(args);   将 args 中的元素拷贝进 c

c.emplace(inits); 使用 inits 构造 c 中的一个元素

c.erase(args);   删除 args 指定的元素

c.clear();      删除 c 中所有元素,返回 void

关系运算符

==,!=      所有容器都支持

<,<=,>,>=    关系运算(无序关联容器不支持)

获取迭代器

c.begin(),c.end()  返回 c 的首元素和尾元素之后位置的迭代器

c.cbegin(),c.cend() 返回 const_iterator

 

反向容器的额外成员(不支持 forward_list)

reverse_iterator       按逆序寻址元素的迭代器

const_reverse_iterator 不能修改元素的逆序迭代器

c.rbegin(),c.rend()      返回指向 c 尾元素和首元素之前位置的迭代器

c.crbegin(),c.crend()   返回 const_reverse_iterator

 

虽然我们可以在容器中保存几乎任何类型,但某些容器操作对元素类型有自己的特殊要求。我们可以为不支持特定操作需求的类型定义容器,但这种情况下就只能使用那些没有特殊要求的容器操作了:

技术分享图片
 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 
 5 class no_default{//no_default 是一个没有默认构造函数的类型
 6     int x;
 7 public:
 8     no_default(int a) : x(a){}
 9     ~no_default(){}
10 };
11 
12 int main(void){
13     int init = 1;
14     std::vector<no_default> v1(10, init);//正确,提供了元素初始化器(执行结果是v1中有10个1)
15     // std::vector<no_default> v2(10);//错误,必须提供一个元素初始化器
16     return 0;
17 }
View Code

 

begin 和 end 成员:

技术分享图片
 1 #include <iostream>
 2 #include <list>
 3 using namespace std;
 4 
 5 int main(void){
 6     list<string> a = {"Milton", "Shakespeare", "Austen"};
 7     auto it1 = a.begin(); //list<string>::iterator
 8     auto it2 = a.rbegin();//list<string>::reverse_iterator
 9     auto it3 = a.cbegin();//list<string>::const_iterator
10     auto it4 = a.crbegin();//list<string>::const_reverse_iterator
11 
12     cout << *it1 << endl;//Milton
13     it1++;
14     cout << *it1 << endl;//Shakespeare
15     *it1 = "jflsfk";
16     cout << *it1 << endl;//jflsfk
17     cout << endl;
18 
19     cout << *it2 << endl;//Austen
20     it2++;
21     cout << *it2 << endl;//jflsfk
22     *it2 = "Shakespeare";
23     cout << *it2 << endl;//Shakespeare
24     cout << endl;
25 
26     cout << *it3 << endl;//Milton
27     it3++;
28     cout << *it3 << endl;//Shakespeare
29     // *it3 = "hello";//错误,常量迭代器类似于具有底层const的指针
30     cout << endl;
31 
32     cout << *it4 << endl;//Austen
33     it4++;
34     cout << *it4 << endl;//Shakespeare
35     // *it4 = "hello";//错误,常量迭代器类似于具有底层const的指针
36     cout << endl;
37 
38     const list<string> b = {"hello", "gg", "yy"};
39     auto it5 = b.begin();
40     auto it6 = b.rbegin();
41     auto it7 = b.cbegin();
42     auto it8 = b.crbegin();
43 
44     cout << *it5 << endl;//hello
45     it5++;
46     cout << *it5 << endl;//gg
47     // *it5 = "aa";//错误,const对象的begin()返回的是一个const_iterator
48 
49     // b = a;//错误
50 
51     list<string> const c = {"hello", "gg", "yy"};//可以将list<string>看作int,那么b和c分别相当于const int 和 int const,是等价的,都是顶层const
52 
53     // c = a;//错误
54 
55     return 0;
56 }
View Code

通过上面的列子还可以发现不以 c 开头的函数都是被重载过的。也就是说,实际上有两个名为 begin 的成员。一个是 const 成员,返回容器的 const_iterator 类型。另一个是非常量成员,返回容器的 iterator 类型。rbegin,end,rend 的情况类型。当我们对一个非常量成员调用这些成员时,返回的是 const 版本。只有在对一个 const 对象调用这些函数时,才会得到一个 const 版本。与 const 指针和引用类似,可以将一个普通的 iterator 转换成对应的 const_iterator,反之则不行。

而以 c 开头版本是 c++11 标准引入的,用以支持 auto 与 begin 和 end 函数结合使用。无论对常量容器还是非常量容器,其返回的都是 const_iterator。

即 auto 与 begin 或 end 结合时,获得的迭代器类型依赖于容器类型,而以 c 开头的版本是可以获得 const_iterator 的,无论容器的类型是否为 const。

 

将一个容器初始化为另一个容器的拷贝:

技术分享图片
 1 #include <iostream>
 2 #include <list>
 3 #include <vector>
 4 #include <deque>
 5 #include <forward_list>
 6 using namespace std;
 7 
 8 int main(void){
 9     list<string> authors = {"Milton", "Shakespeare", "Austen"};//列表初始化
10     vector<const char*> gel = {"a", "an", "the"};//列表初始化
11 
12     list<string> list1(authors);//正确,类型都匹配
13     // deque<string> a(authors);//错误,容器类型不匹配
14     // vector<string> v(gel);//错误,元素类型不匹配
15 
16     forward_list<string> f(gel.begin(), gel.end());//正确,只元素能相互转化即可
17 
18     return 0;
19 }
View Code

为了创建一个容器为另一个容器的拷贝,两个容器的类型及其元素类型必须匹配。不过,当传递迭代器参数来拷贝一个范围时,就不要求容器类型是相同的了。而且,新容器和原容器中的元素也可以不同,只要能将拷贝的元素转换为要初始化的容器元素类型即可。

 

标准库 array:

技术分享图片
 1 #include <iostream>
 2 #include <array>
 3 using namespace std;
 4 
 5 int main(void){
 6     array<int, 10> a;//类型为保存10个int的数组
 7     array<string, 10> b;//类型为保存10个string的数组
 8 
 9     array<int, 10>::size_type i;//数组类型包括元素类型和大小
10     // array<int>::size_type j;//错误,array<int>不是一个类型
11 
12     array<int, 3> c = {1, 2, 3};//列表初始化
13     array<int, 5> d = {1};//d[0]为1,剩余元素都为0
14     //这两点和内置数组是一样的
15 
16     int e[5] = {1, 2, 3, 4, 5};
17     // int cpy[5] = e;//错误,内置数组不支持拷贝或赋值
18     array<int, 5> f = {1, 2, 3, 4, 5};
19     array<int, 5> copy = f;//正确,只要数组类型匹配即合法
20     // copy = e;//错误,内置数组不能被赋值给array
21     // array<int, 4> g = f;//错误,数组大小不一致
22 
23     return 0;
24 }
View Code

定义以及使用 array 类型时必须指定容器的元素类型和大小

数组类型包含元素的类型和大小,因此拷贝初始化时不仅要求初始值的类型必须与要创建的容器相同,大小也要相同

array 的列表初始化规则和内置数组一致

 

assign:

技术分享图片
 1 #include <iostream>
 2 #include <list>
 3 #include <vector>
 4 using namespace std;
 5 
 6 int main(void){
 7     list<string> names;
 8     vector<const char*> old_style;
 9     // names = old_style;//错误,容器类型不匹配不能赋值
10     names.assign(old_style.cbegin(), old_style.cend());//正确,可以将const char*转化成string
11 
12     list<string> a(1);//一个元素,为空string
13     a.assign(10, "hello");//10个元素,每个都是hello
14     for(const auto indx : a){
15         cout << indx << " ";
16     }
17     cout << endl;
18 
19     a.clear();
20     a.insert(a.begin(), 10, "hello");//通过输出可以发现这两条语句与前面的两条语句等效
21     for(const auto indx : a){
22         cout << indx << " ";
23     }
24     cout << endl;
25     return 0;
26 }
View Code

赋值运算符要求左边和右边的运算对象具有相同的类型。此外顺序容器(array除外)还定义了一个名为 assign 的成员,允许我们从一个不同但相容的类型赋值,或从容器的一个子序列赋值。

需要注意的是:赋值相关运算会导致指向左边容器内部的迭代器、引用和指针失效。

 

swap:

技术分享图片
 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 
 5 int main(void){
 6     std::vector<string> v1(10, "f");
 7     std::vector<string> v2(24, "a");
 8     auto it = v1.begin();//此时it指向v1的第一个元素,是f
 9     cout << &it << endl;//0x6afeb8
10 
11     swap(v1, v2);
12 
13     cout << *it << endl;//f
14     cout << &it << endl;//0x6afeb8
15     
16     for(const auto indx : v1){//输出24个a
17         cout << indx << endl;
18     }
19     cout << endl;
20     for(const auto indx : v2){//输出10个f
21         cout << indx << endl;
22     }
23     return 0;
24 }
View Code

除 array 外,swap 不会对任何元素进行拷贝、删除或插入操作,因此可以保证在常数时间内完成。元素不会被移动也意味着,除 string 外,指向容器的迭代器、引用和指针在 swap 操作之后都不会失效。它们任指向 swap 操作之前所指向的那些元素,但是这些元素已经不属于原来的容器了。

 

insert:

注意:只有 vector、deque、list、string 支持 insert 成员

技术分享图片
 1 #include <iostream>
 2 #include <list>
 3 #include <vector>
 4 using namespace std;
 5 
 6 int main(void){
 7     vector<string> a;
 8     list<string> b;
 9 
10     //在指定位置之前插入某个元素
11     b.insert(b.begin(), "hello");//等价于b.push_front("hello");
12     a.insert(a.begin(), "hello");//vector不支持push_front,但我们通过insert达到了同样的效果
13 
14     //在指定位置之前插入范围内元素
15     vector<string> v = {"kjf", "fksl", "fjsl", "fls"};
16     b.insert(b.begin(), v.end() - 2, v.end());//将v中最后两个元素插入到b的开始位置
17     b.insert(b.end(), {"fjksl", "fskl", "fsl"});//在b的末尾插入一个列表
18 
19     list<string> c;
20     auto it = c.begin();
21     string s;
22     while(cin >> s){
23         it = c.insert(it, s);//返回插入元素的迭代器
24     }
25     return 0;
26 }
View Code

需要注意的是:向一个 vector、string 或 deque 插入元素会使原来的迭代器、引用和指针失效。

 

emplace:

技术分享图片
 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 
 5 class gel{
 6 friend ostream& operator<<(ostream&, const gel&);
 7 
 8 private:
 9     int x, y, z;
10 
11 public:
12     gel(int a, int b, int c) : x(a), y(b), z(c){}
13     ~gel(){}
14 };
15 
16 ostream& operator<<(ostream &os, const gel &it){
17     os << it.x << " " << it.y << " " << it.z;
18     return os;
19 }
20 
21 int main(void){
22     vector<gel> a;
23     a.emplace_back(1, 2, 3);//隐式使用了构造函数
24     // a.push_back(1, 2, 3);//错误,没有接受3个参数的push_back版本
25     a.push_back(gel(2, 3, 4));//创建一个临时的gel对象传递给push_back
26 
27     // a.emplace_back();//错误,gel类没有默认构造函数
28     a.emplace(a.begin(), 1, 2, 3);//将gel(1, 2, 3)插入到a的首元素前
29 
30     for(const auto indx : a){
31         cout << indx << endl;
32     }
33     return 0;
34 }
View Code

c++11标准引入了三个新成员——emplace_front、emplace 和 emplace_back,这些操作构造而不是拷贝元素。这些操作分别对应 push_back、insert 和 push_back。

注意:emplace 函数在容器中直接构造元素。传递给 emplace 函数的参数必须与元素类型的构造函数匹配。

以上是关于顺序容器2的主要内容,如果未能解决你的问题,请参考以下文章

css有用的代码片段

将Android片段移动到不同的容器无法更改片段的容器ID

顺序容器

Docker删除报错:Error response from daemon: conflict: unable to delete 08b152afcfae (must be forced)(代码片段

将 Fragment 放在前面(无 Fragment Recreation)

如何在 Android 中的特定片段容器中显示片段