主要知识点:
- 容器定义的类型别名;
- 迭代器begin指向容器的第一个元素,end不是指向最后一个元素,而是指向最后一个元素的后面一个位置;
- vector和deque能通过下标索引和迭代器两种方式访问,list不同使用下标索引访问,只能通过迭代器访问;
- const容器在初始化以后,其中的元素无法修改,且使用迭代器方位时,不用用iterator,而要用const_iterator;
- 容器大小操作(可能会使迭代器失效),(编程中不要缓存迭代器的值);
- c.size();
- c.max_size(); // 容器 c 能容纳的最大size
- c.empty(); // 判断容器 c 是否为空;
- c.resize(n); // 将容器 c resize 成大小为 n 的容器,若 n > c.size(), 该操作原来c中内容不变,后面填充0;若 n <= c.size(), 则取容器 c 前面的 n 个元素。
- c.resize(n, t); 将容器 c resize 成大小为 n,值为 t 的容器,操作逻辑同上;
- 顺序容器的访问:
- 通过迭代器访问
- c.back();
- c.front();
- c[n]; c.at(n); //这两种方式需要下标,所以只使用与vector和deque
- 在顺序容器中添加元素(需要注意,添加元素可能会使迭代器失效):
- 对于vector,v.push_back(); v.insert();
- 对于list,l.push_front(); l.push_back(); l.insert();
- 对于deque, d.push_front(); d.push_back(); d.insert();
- 关于insert,
c.insert(p, t); 在位置 p(p为迭代器)插入元素 t;
c.insert(p, n, t); 在位置 p 插入 n 个 t;
c.insert(p, b, e); 在位置 p 插入 从 b 到 e 之间的所有元素;(b,e为迭代器的起始和终止位置)
- 删除元素:
c.erase§; // 删除迭代器 p 所指向的元素;
c.erase(b, e); // 删除迭代器 b(包括b) 到 e(不包括e) 之间的算有元素,即前闭后开区间;
c.clear();
c.pop_back(); // 删除最后元素
c.pop_front(); // 删除最前面的元素,vector没有该方法
#include<iostream>
#include<vector>
#include<deque>
#include<list>
#include<algorithm>
template <typename Container>
void printContainer(const Container& con)
{
for (Container::const_iterator con_it = con.begin(); con_it != con.end(); ++con_it)
std::cout << *con_it << " ";
std::cout << "\n------------------------------" << std::endl;
}
int main()
{
std::vector<int> v;
std::list<int> l;
std::deque<int> d;
// 1:
std::vector<int>::size_type v1;
std::vector<int>::iterator v2;
std::vector<int>::const_iterator v3; // 常迭代器
std::vector<int>::reverse_iterator v4; // 逆序迭代器,从后向前迭代
std::vector<int>::const_reverse_iterator v5; // 常逆序迭代器
std::vector<int>::difference_type v6; // 用来保存两个迭代器之间的距离,类似数组下标索引之差
//std::vector<int>::value_type v7;
//std::vector<int>::reference v8;//引用
//std::vector<int>::const_reference v9;//常引用
std::list<int>::size_type l1;
std::list<int>::iterator l2;
std::list<int>::const_iterator l3;
std::list<int>::reverse_iterator l4;
std::list<int>::const_reverse_iterator l5;
std::list<int>::difference_type l6;
//std::vector<int>::value_type v7;
//std::vector<int>::reference v8;
//std::vector<int>::const_reference v9;
std::deque<int>::size_type d1;
std::deque<int>::iterator d2;
std::deque<int>::const_iterator d3;
std::deque<int>::reverse_iterator d4;
std::deque<int>::const_reverse_iterator d5;
std::deque<int>::difference_type d6;
//std::deque<int>::value_type d7;
//std::deque<int>::reference d8;
//std::deque<int>::const_reference d9;
// 2,3:
v.push_back(1); v.push_back(2); v.push_back(3); v.insert(v.begin(), 0); //不能 v.push_front()
l.push_back(10); l.push_back(20); l.push_back(30); l.push_front(0); l.insert(l.end(), 40);
d.push_back(100); d.push_back(200); d.push_back(300); d.push_front(0);; d.insert(d.end(), 400);
// 访问输入:
for (std::vector<int>::size_type i = 0; i < v.size(); ++i) // vector,可通过下标索引和迭代器两种方式访问;
std::cout << v[i] << " ";
std::cout << std::endl; // 0 1 2 3
for (std::list<int>::iterator iter = l.begin(); iter != l.end(); ++iter) // list,只能通过迭代器访问;
std::cout << *iter << " "; // 其中迭代器begin指向l的第一个元素,end不是指向最后一个元素,而是指向最后一个元素的后面一个位置
std::cout << std::endl; // 0 10 20 30 40
for (std::deque<int>::size_type i = 0; i < d.size(); ++i) // deque,可通过下标索引和迭代器两种方式访问;
std::cout << d[i] << " "; // 0 100 200 300 400
std::cout << std::endl;
std::cout << "逆序迭代器rbegin, rend: ";
for (std::deque<int>::reverse_iterator iter = d.rbegin(); iter != d.rend(); ++iter) // 链表,只能通过迭代器访问;
std::cout << *iter << " "; // 400 300 200 100 0
std::cout << std::endl;
// 4:
const std::vector<int> cv(v); //const 容器不能修改其中的元素,cv.push_back(123); cv[1] = 123等方式均不合法。
for (std::vector<int>::const_iterator citer = cv.begin(); citer != cv.end(); ++citer) //对于const 容器,使用迭代器访问时必须用常迭代器const_iterator,否则报错
std::cout << *citer << " "; // 0 1 2 3
std::cout << std::endl;
// 5:
std::cout << "容器l总的max_size:" << l.max_size() << std::endl;
if (l.empty())
std::cout << "容器l为空!\n";
else
std::cout << "容器l中总的元素个数:" << l.size() << std::endl;
l.resize(10); // 原来为:0 10 20 30 40, resize后原来数量不变,后面新增的数为0,结果为: 0 10 20 30 40 0 0 0 0 0
printContainer(l);
l.resize(15, -1); // 原来数据不变,新增的数据用-1填充;
printContainer(l); // 结果为: 0 10 20 30 40 0 0 0 0 0 -1 -1 -1 -1 -1
l.resize(5);
printContainer(l); // 结果为:0 10 20 30 40
l.resize(0);
if (l.empty())
std::cout << "now,容器l为空!\n";
l.push_back(0); l.push_back(10); l.push_back(20); l.push_back(30); l.push_back(40);
//6.1. 使用迭代器访问 *iter
std::cout << *(--d.end()) << " " << *d.begin() << std::endl; // 400 0
//6.2-3:
std::cout << l.front() << " " << l.back() << std::endl; // 0 40; // 若容器为空,则报错
//通过引用访问
std::vector<int>::reference rf = v.front();
std::vector<int>::reference rb = *--v.end();
std::cout << rf << " " << rb << std::endl; // 0 3
//5.4: 使用下标,不能用于list
std::cout << d[0] << " " << v[1] << std::endl; // 0 1; // 若下标越界,不会抛异常,直接报错
std::cout << d.at(2) << " " << v.at(3) << std::endl; // 200 3; // 若下标越界,会抛异常
try{
std::cout << v.at(10) << std::endl;
}
catch (std::out_of_range) {
std::cout << "错误,下标越界!\n";
}
// 7: 请参考https://blog.youkuaiyun.com/sunqin_csdn/article/details/84929609
// 对于list
std::list<int>::iterator ibegin = l.begin();
//std::list<int>::iterator iend = l.end(); // 要避免存储end返回的迭代器,因为存储的迭代器可能会在更新元素后失效
//while (ibegin != iend) //错误,在插入新的元素之后iend不再指向l.end
while (ibegin != l.end())
{
l.insert(ibegin, 123);
++ibegin;
} // 结果:
printContainer(l); // 123 0 123 10 123 20 123 30 123 40
// 对于vector和deque
std::deque<int>::iterator ibegin2 = d.begin();
// std::deque<int>::iterator iend2 = d.end();
int i = 0;
//while (ibegin != iend) //错误,在插入新的元素之后iend不再指向d.end
while (ibegin2 != d.end() && i < 5)
{
ibegin2 = d.insert(ibegin2, 123);
++ibegin2; ++i;
} // 每次插入新的元素之后,ibegin的位置更新了,最终结果为:123 123 123 123 123 0 100 200 300 400
printContainer(d);
std::vector<int>::iterator ibegin3 = v.begin();
while (ibegin3 != v.end())
{
ibegin3 = v.insert(ibegin3, 123);
++ibegin3; ++ibegin3;
} // 每次插入新的元素之后,ibegin的位置更新了,最终结果为:123 0 123 1 123 2 123 3
printContainer(v);
// 8, 删除:
l.pop_back(); l.pop_front();
printContainer(l); // 0 123 10 123 20 123 30 123
v.erase(find(v.begin(), v.end(), 1)); // 若v中没有元素1,则返回的迭代器指向v.end();此时程序会报错
printContainer(v); // 123 0 123 123 2 123 3
std::deque<int>::iterator it1 = find(d.begin(), d.end(), 123);
std::deque<int>::iterator it2 = find(d.begin(), d.end(), 100);
d.erase(it1, it2);
printContainer(d); // 100 200 300 400
// v.pop_front(); //错误,vector没有该方法
v.clear(); // 清空v中的所有元素:
std::cout << "now, 容器v总的元素个数:" << v.size() << std::endl;
//system("pause");
return 0;
}
- 赋值与交换:
- c1 = c2; // 将 c2 中的元素复制到 c1 中,直接覆盖;两种容器类型必须相同;
- c.assign(b, e); // 使用 assign 赋值,两种容器类型兼容即可;
- c.assign(n, t); // 将 c 替换 n 个值为 t 的元素;
- c1.swap(c2); // 将 c1 与 c2 中的元素交换;类型必须相同;
#include<iostream>
#include<vector>
#include<deque>
#include<list>
#include<string>
#include<algorithm>
template <typename Container>
void printContainer(const Container& con, std::string str)
{
std::cout << str.c_str();
for (Container::const_iterator con_it = con.begin(); con_it != con.end(); ++con_it)
std::cout << *con_it << " ";
std::cout << "\n------------------------------" << std::endl;
}
int main()
{
std::vector<int> v1, v2, v3;
// 9:
v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4);
v2.push_back(10); v2.push_back(20); v2.push_back(30);
v3.push_back(100); v3.push_back(200); v3.push_back(300); v3.push_back(400); v3.push_back(500); v3.push_back(600);
v1.swap(v2);
printContainer(v1, "now, v1容器中的元素:"); // 10 20 30
printContainer(v2, "now, v2容器中的元素:"); // 1 2 3 4
v2 = v1;
printContainer(v2, "now, v2容器中的元素:"); // 10 20 30
v1.assign(v3.begin(), --v3.end());
printContainer(v1, "now, v1容器中的元素:"); // 100 200 300 400 500
std::vector<std::string> vs;
std::list<char *> vc;
vs.push_back("string "); vs.push_back("hello "); vs.push_back("world ");
vc.push_back("C++"); vc.push_back("C"); vc.push_back("python"); vc.push_back("java");
vs.assign(vc.begin(), vc.end());
//vc.assign(vs.begin(), vs.end()); // 错误,string类型不能转成字符指针 char*
printContainer(vs, "now, vs容器中的元素:"); // C++ C python java
vc.assign(10, "Hello ");
vs.assign(5, "World! ");
printContainer(vs, "now, vs容器中的元素:"); // World! World! World! World! World!
printContainer(vc, "now, vc容器中的元素:"); // Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello
//system("pause");
return 0;
}
- 关系运算符,所有容器类型均可使用,比较容器必须具有相同的容器类型,基于容器内元素的比较,容器内元素必须有相应的关系运算符(eg. 若两个vector<类>相比较,则该类之间必须能相互比较,即该类重载了相应的运算符)。
#include<iostream>
#include<vector>
#include<deque>
#include<list>
#include<string>
#include<algorithm>
int main()
{
std::vector<int> v1 = { 1,3,5,7,9, 10 }, v2 = {0, 2,4,6,8,12,23};
std::vector<int> v3(v1);
// 10:
if (v1 > v2)
std::cout << "容器v1大!\n";
v2.erase(v2.begin()); // 删除v2第一个元素;
if (v1 < v2)
std::cout << "容器v2大!\n";
if (v1 == v3)
std::cout << "容器v1和v3一样大!\n";
//vector<int> 类型容器和vector<double> 以及 list<int> 等因为类型不一样,都不能比较;
// 个人感觉容器比较和字符串比较大小的逻辑完全一样!!!
// 若两个vector<类>相比较,则该类之间必须能相互比较,即该类重载了相应的运算符;
system("pause");
return 0;
}
- 顺序容器的选取,vector,list,deque的优缺点;根据插入访问等操作需要的时间来判断:
- vector较list缺点:
- vector push_back数据时,若其容量满了,则需要创建一个更大的数组,将原来的内容拷贝进去,然后在添加该元素,所以有些时候vector容器在push_back时会比较慢,但是list就不会有这种情况,list在任何时候push_back都很快;
- 对于insert,vector会把相应的元素全部向后移动,然后才把该元素插入到相应的位置,所以理论上vector的insert速度比list的插入速度要慢;
- 对于erase与insert同理;
- vector较list优点:
- 在sort和find操作时,vector等可以通过快速排序和二分查找,排序和查找速度很快,但list容器操作速度相对较慢;
- deque也能使用快速排序和二分查找(因为能通过下标索引访问元素)的所有操作都比vector稍微慢,但deque可以在两端操作,而vector只能在末端操作,deque操作更灵活。