c++基础——容器
Posted konghuzi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++基础——容器相关的知识,希望对你有一定的参考价值。
1. 顺序容器
- vector和string将元素保存在连续的内存空间中。由于元素是连续存储的,由元素的下标来计算其地址是非常快速的。但是在这两种容器的中间位置添加或删除元素就非常耗时
- list和forward_list两个容器的设计目的是令容器任何位置的添加和删除操作都很快速。作为代价,这两个容器不支持元素的随机访问
- deque是一个更为复杂的数据结构。与string和vector类似,deque支持快速的随机访问。在deque的中间位置添加或删除元素的代价(可能)很高。但是,在deque的两端添加或删除元素都是很快的,与list和forward_list添加删除元素的速度相当
- forward_list没有size操作
1.1 容器的定义和初始化
每个容器类型都定义了一个默认构造函数。除array之外,其他容器的默认构造函数都会创建一个指定类型的空容器,且都可以接受指定容器大小和元素初始值的参数。
1.1.1 将一个容器初始化为另一个容器的拷贝
主要有两种方式,①直接拷贝整个容器,②(array除外)拷贝由一个迭代器对指定的元素的范围。
1 #include<vector> 2 #include<string> 3 #include<deque> 4 #include<forward_list> 5 #include<list> 6 #include<iostream> 7 using namespace std; 8 9 int main() 10 list<string> authors = "tom", "Mike", "Bob" ; 11 vector<const char*> articles = "ad", "mi" ,"po" ; 12 13 list<string> authors2(authors); 14 //vector<string> articles2(articles);//错误,类型不一致 15 16 /*forward_list 17 单向list,不能反向迭代(list可以) 18 不能使用size函数,但是可以使用迭代器的distance来计算 19 但是 20 forward_list的操作比list容器块,且内存占用少 21 */ 22 23 forward_list<string> articles3(articles.begin(), articles.end()); 24 cout << distance(articles3.begin(), articles3.end()) << endl; // 3d 25 26 getchar(); 27 return 0; 28
1.1.2 与顺序容器大小相关的构造函数
只有顺序容器的构造函数才接受大小参数,关联容器不支持。
1 vector<int> ivec (10, -1); //10个int元素,每个都初始化为-1 2 list<string> svec(10,"hi"); //10个string,每个够初始化为“hi" 3 forward_list<int> ivec(10); //10个元素,每个都初始化为0 4 deque<string> svec(10); //10个元素,每个都是空string
1.1.3 标准库array具有固定大小
array 是提供的新容器,比内置数组更安全。两者一定区别如下:
1 // 内置数组初始化 2 int a[5]; 3 int b[] = 1,2,3 ; 4 int c[5] = 1,2,3 ;//其他值默认初始化 0 5 // 访问数组下标时,可以非 size_type 类型,只要不越界。但是容器不行 6 int *ptr = &a[5]; 7 cout << ptr[-3] << endl; 8 9 // 数组容器 10 array<int, 5> a1; 11 array<int, 5> b1 = 1,2,3, ; 12 // 但是容器能拷贝复制,只要容器 类型+大小 一致 13 array<int, 5> c1 = b1; 14 //a1 = b1; //但是不能直接拷贝 15 // 容器的遍历遵循普通容器遍历方式
1.2 顺序容器操作
1.2.1 向顺序容器添加元素
- push_back : 除了array 和 forward_list 之外,每个顺序容器(包括string 类型),都支持
- 除了push_back之外,list、forward_list、deque 还支持名为 push_front 的操作
- 在容器特定位置添加元素:
vecotr,deque,list,string 支持 insert(iter, value) 的操作(forward_list 有自己的 insert 的操作),所以,即使 vector 没有 push_front 操作,但是可:
1 vector<string> vct; 2 vct.insert(vct.begin(), "test"); // vector 不插入末尾,则效率很慢
- 插入范围元素:
// 接受一个元素数目和元素值 vct.insert(vct.end(), 4, "qaq"); //在end处插入,个数为4,初始化为qaq的字符串 // 接受一对迭代器 vct.insert(vct.end(), vct2.end() - 2, vct2.end());//插入位置,vct2给定范围 // 接受位置和列表 vct.insert(vct.end(), "2333", "xswl" );
// 插入的列表不能指向目标容器
// vct.insert(vct.end(), vct.begin(), vct.end()); // 错误
- 使用 insert 的返回值。insert 返回值为指向这个新元素
1 list<string> lst; 2 auto iter = lst.begin(); 3 while (cin >> word) 4 iter = lst.insert(iter, word); //等价于调用push_front
- emplace
1 //在c的末尾构造一个Sales_data对象 2 //使用三个参数的Sales_data构造函数 3 c.emplace_back("978-9897897987", 25, 15.99); 4 //错误:没有接受三个参数的push_back版本 5 c.push_back("978-9897897987", 25, 15.99); 6 //正确:创建一个临时的Sales_data对象传递给push_back 7 c.push_back(Sales_data("978-9897897987", 25, 15.99));
1.2.2 访问元素
包括 array 在内的每个顺序容器中,都有一个 front 成员函数,而除 forward_list 之外,所有顺序容器都有 back 成员函数。
、
1 // 解引用一个迭代器时,要判空 2 if (!lst.empty()) 3 auto val1 = *lst.begin(), val2 = lst.front(); 4 5 auto last = lst.end(); 6 auto val3 = *(--last);// forward_list 迭代器不能递减 7 auto val4 = lst.back();// forward_list 不支持 8
下面再看:
1 /* 2 front back 都是返回引用 3 */ 4 5 int main() 6 7 // 数组容器 8 array<int, 5> a1; 9 array<int, 5> b1 = 1,2,3, ; 10 11 b1.front() = 9; // b1 初始值改变 12 int &c = b1.back(); 13 c = 99; // b1 末尾值改变 14 15 // auto 会吃引用 16 auto a2 = a1.front(); 17 a2 = 999; // 无效,可通过 auto &a2 18 19 getchar(); 20 return 0; 21
1.2.3 删除元素
vector 和 string 不支持 push_front 所以不支持 pop_front。同时 vct.clear() 等同于 vct.erase(vct.begin(), vct.end())。
1.2.4 删除插入更新迭代器
insert 之后返回的是新插入的元素的迭代器,erase之后返回的是被删除的后一个元素的迭代器(此时注意end)。
1 int main() 2 // 删除所有偶数元素,复制所有奇数元素 3 vector<int> v = 0,1,2,3,4,5,6,7,8,9 ; 4 auto iter = v.begin(); 5 6 while (iter != v.end()) 7 if (*iter % 2) 8 iter = v.insert(iter, *iter); 9 iter += 2; 10 11 else 12 iter = v.erase(iter); 13 14 15 16 getchar(); 17 return 0; 18
不要保存容器的end迭代器,当容器增加或者删除元素时,end迭代器也会失效,所以应该通过 v.end() 来判断,而不应保存至某一个局部变量。
1.2.5 构造 string 的其他方法
1 2 int main() 3 char ch[] = ‘a‘,‘b‘,‘c‘,‘1‘,‘2‘ ;//不空字符结尾 4 string s(ch, 5); 5 cout << s << endl; //abc12 6 7 string s2(s, 1); // (string,pos) 从pos位置开始拷贝 8 cout << s2 << endl;// bc12 9 10 string s3(s, 1, 2); // (string,start,length) 11 cout << s3 << endl; // bc 12 13 /* 14 通常当我们从一个 const char* 创建 string 时,指针指向的数组必须以空字符(‘/0‘)结尾,拷贝 15 操作遇到空字符时停止。 16 如果我们传递给构造函数拷贝长度,则无所谓 17 */ 18 const char *cc = "qaq23333";//以空字符结尾 19 string s4(cc); 20 string s5(ch); // ch 不是以空字符结尾,即未定义 21 22 cout << s4 << "\\t" << s5 << endl;//打印s5出现奇怪字符 23 24 25 getchar(); 26 return 0; 27
1.2.6 string操作——substr,insert,erase
1 #define Log(s) cout << s << endl; 2 int main() 3 4 string str("233qaq"); 5 6 /// substr 7 string s2 = str.substr(2); 8 Log(s2); // 3qaq 9 s2 = str.substr(0, 2); 10 Log(s2);//23 11 12 /// insert 13 str.insert(2, s2); // 下表版本插入 14 Log(str); //23233qaq 15 str.insert(str.end(), ‘h‘);// 迭代器版本插入 16 Log(str); //23233qaqh 17 str.insert(0, s2, 1, 1);// 插入3 18 Log(str); //33233qaqh 19 str.insert(0, "hi"); 20 Log(str); //hi33233qaqh 21 22 /// erase 23 string str("This is an example sentence."); 24 str.erase(10, 8); // 从下标10开始,erase 8位 25 Log(str); // "This is an sentence." 26 str.erase(str.begin() + 9); // begin迭代器开始第九位 27 Log(str); // "This is a sentence." 28 str.erase(str.begin() + 5, str.end() - 9); 29 Log(str)// "This sentence." 30 31 32 33 getchar(); 34 return 0; 35
1.2.7 string操作——append,replace
1 #define Log(s) cout << s << endl; 2 int main() 3 string s("hello"), s2 = s; 4 s.insert(s.size(),",world"); 5 s2.append(",world"); // 同上,效果一致 6 Log(s); 7 Log(s2); 8 9 s.erase(5, 6); // 从下标5开始,erase 6 位 10 s.insert(5, ",Bob"); 11 s2.replace(5, 6, ",Bob"); // 效果同上两句话一致 12 Log(s); 13 Log(s2); 14 15 getchar(); 16 return 0; 17
以上是关于c++基础——容器的主要内容,如果未能解决你的问题,请参考以下文章