总结一些stl的知识,一起学习
序列容器:vector、deque、string和list
关联容器:set、map、multimap和multiset
连续内存容器:vector、deque、string,新元素插入或者已存元素被删除,其他在同意内存块的元素就必须向上或者向下移动来为新元素提供空间或者填充原来被删除的元素所占的空间
节点的容器:list,在每个内存块中只保存一个元素,插入或者删除时,不需要移动
序列容器:
1、可以任意位置插入一个新元素
2、如果需要随机访问迭代器,选择vector、deque、string
3、插入或者删除数据试,如果不想容器内现有元素的移动,需要放弃连续内存容器
4、需要把迭代器、指针、引用的失效次数降低到最少,应使用节点容器,这些容器上进行插入和删除不会是迭代器、指针和引用失效(除非他们指向你删除的元素)
5、随机访问迭代器,没有删除并且插入只发生在容器结尾,指针和引用的数据就不会失效
vector是一种可以默认使用的序列类型,需要频繁的对序列中部进行插入和删除时,应该使用list,当大部分插入和删除发生在序列的头尾时,可以使用deque
一、typedef的使用
typedef是其他类型的同义词,所以提供的封装是纯的语法(书上说是词法)
typedef和define的区别
1、用法不一样typedef是定义数据类型的别名,define用于定义常量或者复杂的宏
2、执行时间不同,typedef是编译过程的一部分,能够在编译期间进行类型检查,define是预编译的部分,发生在编译之前,只是简单的字符串替换、不会进行类型检查
3、作用域不同,typedef有作用于的限制,定义的别名只在作用域范围内可用,define不受作用域的约束
4、针对指针的操作不同,了解即可
5、内存占用不同,typedef定义类型别名不会产生额外的内存占用,只是为已经有的类型赋予一个新的名称,define定义的宏在预处理截断进行文本替换,会产生额外的代码和内存再用
二、使容器里面对象的拷贝操作轻量并且正确
容器保存对象,但是不是我们给他放进去时的对象,而是这个对象的拷贝,反之从容器中获取到的对象也是这样,拷贝进去,拷贝出来,STL中只要移动对象,就会拷贝
如果想解决因为拷贝产生的分割问题(将派生类对象插入容器,拷贝函数是基类的拷贝函数,对象的派生类部分会被删除),方法就是建立指针的容器,而不是对象的容器
三、区间函数代替单元素函数
vector、string、deque和list标准序列容器都有效的一个函数assign,插入的数据是另一个有序区间的数据时,使用assign比循环执行push_bacl好的太多。并且不会造成效率上的损失
四、容器内保存的是new的指针时候,需要在容器销毁前,delete那些指针
容器销毁会执行自己的析构函数,但是析构函数不会执行将每个指针元素delete的操作,需要编写代码的人自己来处理,以防内存泄露
五、选择删除函数
1、连续内存容器:vector、deque、string,最好的方法是erase-remove删除方法
c.erase(remove(c.begin(), c.end()),1963), c.end()) remove先把找到的元素放到尾端,再用erase进行删除
2、list容器直接使用remove删除即可
3、标准关联容器:set、map、multiset、multimap是没有remove函数的,删除方式是erase
对于序列容器而言,erase一旦删除完成,他就是指向紧接在被删元素之后的元素的有效迭代器
对于关联容器而言,erase的返回类型是void,
六、按照特定判定式去删除一个容器中的元素
1、vector、string、deque使用erase-remove_if
vector<int> vec = {5,10,15,25,35}; vec.erase(remove_if(vec.begin(), vec.end(), [](int i){return i > 15;}), vec.end()); for(auto it : vec) { cout << it << endl; }
2、list使用remove_if
3、关联容器使用remove_copy_if和swap
std::map<int, int> originalMap = {{1, 5}, {2, 12}, {3, 8}, {4, 15}, {5, 9}}; std::map<int, int> copiedMap; std::remove_copy_if(originalMap.begin(), originalMap.end(), std::inserter(copiedMap, copiedMap.end()),[](const std::pair<int, int>& pair) { return pair.second <= 10; }); // 输出复制后的容器 for (const auto& pair : copiedMap) { std::cout << pair.first << ": " << pair.second << std::endl; } originalMap.swap(copiedMap); for (const auto& pair : originalMap) { std::cout << pair.first << ": " << pair.second << std::endl; }
七、循环内执行操作时,需要注意
1、如果是标准序列容器,写一个循环遍历容器,每当调用erase时,记得用它的返回值更新迭代器
vector<int> v1{1,2,3,34,5,65,76,3,3}; auto iter = v1.begin(); for(;iter!= v1.end(); iter++) { if(*iter == 3) { iter = v1.erase(iter); } } for(auto itit : v1) { cout << itit << endl; } return 0;
2、如果容器是标准关联容器,写一个循环来遍历容器元素,当你把迭代器传给erase时,记得后置递增他
map<int,string> m1; m1[1] = 'a'; m1[2] = 'b'; m1[3] = 'c'; m1[4] = 'd'; m1[5] = 'e'; m1[6] = 'f';
for(auto it = m1.begin(); it != m1.end(); it++)
{
if(it->first == 3)
{
m1.erase(it++);
cout << it->first << endl;
}
}
for(auto iter : m1)
{
cout << iter.first << endl;
}