一、容器
1.1 容器的相关概念(掌握)
泛型编程是一种编程理念,其提出的动机是发明一种语言机制,能够在编程中编写完全一般化的可重复使用的算法,不会因为数据类型的不同而有差异。
美国的惠普实验室针对泛型编程开发了一系列软件工具,被称为标准模板库(STL),后续在C++中被广泛引入。STL中绝大多数的类型都使用了模板的编程技术,这相比传统的代码而言有更好重用特性。
在STL中最核心的内容是容器和迭代器。
容器指的是用来存储数据的集合,由于内部存储的数据使用模板实现,因此可以支持绝大多数数据类型。迭代器是配合容器进行高效遍历的工具,是一种特殊的指针。
容器类自动申请和释放内存,无需new和delete操作。
容器的分类如下:
容器类的使用需要引入头文件,所有的容器类都是属于std名字空间。
1.2 顺序容器
顺序容器是一种各元素之间有顺序关系的结构集合,每个元素在容器都有固定的位置,但是可以用删除或插入等操作改变这个位置。
1.2.1 array 数组(熟悉)
array是C++ 11中引入的容器类型,与传统数组相比,array更加安全和易于使用。
#include <iostream>#include <array> // 引入头文件using namespace std;int main(){ // 创建一个长度为5的int数组 array<int,5> arr = {2,4,6,8,10}; cout << arr[0] << endl; // 2 cout << arr[4] << endl; // 10 arr[1] = 666; cout << arr.at(1) << endl; // 666 // 普通循环 for(int i=0;i<arr.size();i++) { cout << arr.at(i) << " "; } cout << endl; // 给所有元素赋值 arr.fill(5); // for-each循环 for(int i:arr) { cout << i << " "; } cout << endl; // 迭代器:略 return 0;}
1.2.2 vector 向量(掌握)
vector内部由数组实现,因此可以高效地进行元素的随机存取,但是插入和删除的效率较低,是最常用的一种顺序容器。
#include <iostream>#include <vector> // 引入头文件using namespace std;int main(){ // 创建一个长度为0的向量对象 vector<int> vec1; cout << vec1.empty() << endl; // 1 // 创建一个长度为5的向量对象 vector<int> vec2(5); vec2[0] = 123; // 修改元素 cout << vec2[0] << endl; // 123 cout << vec2.at(4) << endl; // 0 // 5个666 vector<int> vec3(5,666); // 向后追加元素 vec3.push_back(888); // 在第二个位置插入2 // 参数1:插入的位置 // 参数2:插入的元素 vec3.insert(vec3.begin()+1,2); // 在倒数第二个位置插入222 vec3.insert(vec3.end()-1,222); // 删除第一个元素 vec3.erase(vec3.begin()); // 删除最后一个元素 vec3.erase(vec3.end()-1); vec3.pop_back(); // 删除最后一个元素 // 普通循环遍历 for(int i=0;i<vec3.size();i++) { cout << vec3.at(i) << " "; } cout << endl; // 清空 vec3.clear(); // for-each遍历 for(int i:vec3) { cout << i << " "; } cout << endl; // 迭代器遍历:略 return 0;}
1.2.3 list 列表(掌握)
list内部由双向链表实现,因此元素的内存空间是不连续的,能高效地进行插入和删除操作,但是随机存取的效率较低,并且list只能通过迭代器指针来访问数据。
#include <iostream>#include <list> // 引入头文件using namespace std;int main(){ // 创建一个四个hello元素的列表对象 list<string> lis1(4,"hello"); list<string> lis2; // 判断是否为空 cout << lis2.empty() << endl; // 向后追加元素 lis1.push_back("bye"); // 向前追加元素 lis1.push_front("start"); // 取出头部元素 cout << lis1.front() << endl; // 取出尾部元素 cout << lis1.back() << endl; // 删除头部元素 lis1.pop_front(); // 删除尾部元素 lis1.pop_back(); // 在第二个位置插入元素"2B" lis1.insert(++lis1.begin(),"2B"); // 在倒数第二个位置插入元素"2Y" lis1.insert(--lis1.end(),"2Y"); // 保存迭代器指针 list<string>::iterator iter = lis1.begin(); // 修改第一个元素 *iter = "One"; // 取出元素 cout << *iter << endl; // 移动迭代器指针 // 参数1:要移动的迭代器指针对象 // 参数2:移动的距离 advance(iter,3); // 在第四个元素的位置插入"4544" lis1.insert(iter,"4544"); // 删除倒数第一个元素 lis1.erase(--lis1.end()); // 清空// lis1.clear(); // 排序 lis1.sort(); // 不支持普通的for循环遍历,但是支持for-each遍历 for(string s:lis1) cout << s << " "; cout << endl; // 迭代器遍历:略 return 0;}
1.2.4 deque 队列(熟悉)
deque的性能位于vector和list之间,是一种性能均衡的容器。另外,deque基本支持所有的常用API接口。
示例代码:略
1.3 关联容器(掌握)
关联容器的各元素之间没有严格的物理上顺序关系,但是内部有排序,因此可以使用迭代器遍历。
关联容器内部的元素数据以键值对(key-value pair)的形式存在。
map与multimap的区别在于,前者是单重键值对映射(一一映射),后者允许一个键对应多个值,map更为常用。
键必须唯一,通常使用字符串类型;值可以重复,可以是任何类型。
#include <iostream>#include <map> // 引入头文件using namespace std;int main(){ // 创建一个内容为空的map对象 map<string,int> map1; // 添加元素 map1["height"] = 180; map1.insert(pair<string,int>("country",86)); map1["salary"] = 10000; map1["age"] = 40; map1["weight"] = 71; // 已经存在这个键值对,因此再次赋值就是修改键对应的值 map1["weight"] = 70; // 删除元素 if(map1.erase("salary")) { cout << "删除成功,还剩下" << map1.size() << "个键值对" << endl; }else { cout << "删除失败!" << endl; } // 迭代器指针 // 如果有对应的键,则指向键值对 // 如果没有对应的键,则执指向“最后一个元素”的后面 map<string,int>::iterator iter = map1.find("salary"); if(iter != map1.end()) { // 取出元素 int value = iter->second; cout << value << endl; }else { cout << "没有对应的值!" << endl; } // 清空 map1.clear(); cout << map1.empty() << endl; return 0;}
1.4 迭代器(掌握)
容器的遍历通常是迭代器进行,因为迭代器对容器遍历的兼容性最好,且遍历效率最高。迭代器是一种特殊的指针,如果要使用迭代器进行读写操作,可以使用iterator类型的迭代器;如果要使用迭代器进行只读操作,可以使用const_iterator类型的迭代器,因为只读迭代器的效率更高。
#include <iostream>#include <array>#include <vector>#include <list>#include <deque>#include <map>using namespace std;int main(){ string str = "ABC678%^"; // string的只读迭代器 for(string::const_iterator iter = str.begin();iter != str.end();iter++) { cout << *iter << " "; } cout << endl; array<double,20> arr; arr.fill(3.14); // array的读写迭代器 for(array<double,20>::iterator iter = arr.begin(); iter!=arr.end();iter++) { cout << ++(*iter) << " "; } cout << endl; vector<int> vec1(4,4); // vector的只读迭代器 vector<int>::const_iterator iter_vec = vec1.begin(); while(iter_vec != vec1.end()) { cout << *iter_vec << " "; iter_vec++; } cout << endl; list<string> lis1; lis1.push_back("fhdjks"); lis1.push_back("fhfggks"); lis1.push_back("dsdsghs"); lis1.push_back("fh***"); // list的读写迭代器 list<string>::iterator iter_lis = lis1.begin(); while(iter_lis != lis1.end()) { cout << (*iter_lis).append("~") << " "; iter_lis++; } cout << endl; deque<int> deq(5,5); // deque的读写迭代器,也可以只读 for(deque<int>::iterator iter = deq.begin(); iter!=deq.end();iter++) { cout << *iter << " "; } cout << endl; map<string,string> map1; map1["城市"] = "济南"; map1["name"] = "Tom"; map1["age"] = "7"; map1["friend"] = "Jerry"; // map的只读迭代器 for(map<string,string>::const_iterator iter = map1.begin(); iter != map1.end();iter++) { cout << iter->first << "-"; // 键 cout << iter->second << endl; // 值 } return 0;}