👉vector 的介绍👈
vector是表示可变大小数组的序列容器。
就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自 动处理。
本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增 长。
与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
使用STL的三个境界:能用,明理,能扩展 ,那么下面学习vector,我们也是按照这个方法去学习。
👉vector 的使用👈
vector学习时一定要学会查看文档:vector的文档介绍,vector在实际中非常的重要,在实际中我们熟悉常见的接口就可以,下面列出了哪些接口是要重点掌握的。
vector 的构造函数和遍历
vector的构造和遍历代码演示
void vectorTest1() { vector<int> first; // empty vector of ints vector<int> second(4, 100); // four ints with value 100 vector<int> third(second.begin(), second.end()); // iterating through second vector<int> fourth(third); // a copy of third // 用数组来初始化 // the iterator constructor can also be used to construct from arrays: int myints[] = { 16,2,77,29 }; vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int)); // []+下标遍历,只有string和vector支持这种方式 for (size_t i = 0; i < fifth.size(); ++i) { cout << fifth[i] << ' '; } cout << endl; // 迭代器遍历 vector<int>::iterator it = fifth.begin(); while (it != fifth.end()) { cout << *it << ' '; ++it; } cout << endl; // 范围for遍历,底层为迭代器 for (auto e : fifth) { cout << e << ' '; } cout << endl; }
vector iterator
为什么vector<char> str
和string str
无法取代对方
网络上发送一段字符串,字符串需要以'\0'结束,而 string 无法被发送。
string 支持比较字符串大小,比较常见;而 vector 的比较大小不常见,也没有什么意义。
函数接口不完全相同。如 string 类有 +=,而 vector 没有 +=。
vector 空间增长问题
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
resize在开空间的同时还会进行初始化,影响size
reserve 函数接口不会使 vector 缩容,缩容一般是异地缩容,缩容比较大。
resize 的函数接口也能说明内置类型需要有构造函数。
注:在程序里看到的空间都是虚拟的进程空间,进程会提高页表映射和物理内存建立映射。只有在内核层通过页表才能找到物理地址。
注:reserve 只会修改capacity,不会影响size。
为什么上面的程序会报错呢?因为 reserve 函数将 capacity改成了10,而 size 还是0,所以就会越界访问,触发了 [ ]运算符重载的 assert 报错。
我们可以将上面的代码,改成下面代码的样子,就不会出现这个问题了。
void vectorTest3() { vector<int> v; v.reserve(10); // 或者将v.reserve(10)换成v.resize(10) for (size_t i = 0; i < 10; ++i) { //v[i] = i; v.push_back(i); } }
测试 vector 的默认扩容机制
void TestVectorExpand() { size_t sz; vector<int> v; sz = v.capacity(); cout << "making v grow:\n"; for (int i = 0; i < 100; ++i) { v.push_back(i); if (sz != v.capacity()) { sz = v.capacity(); cout << "capacity changed: " << sz << '\n'; } } }
VS的运行结果
Linux的运行结果
capacity的代码在VS和g++下分别运行会发现,VS下capacity是按1.5倍增长的,g++是按2倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。VS是PJ版本STL,g++是SGI版本STL。
为什么扩容多数扩2倍
- 2 倍比较合适
- 每次扩少了,会导致频繁扩容
- 每次扩多了,用不完,存在空间浪费
如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够,就可以避免边插入边扩容导致效率低下的问题了。
void TestVectorExpandOP() { vector<int> v; size_t sz = v.capacity(); v.reserve(100); // 提前将容量设置好,可以避免一遍插入一遍扩容 cout << "making bar grow:\n"; for (int i = 0; i < 100; ++i) { v.push_back(i); if (sz != v.capacity()) { sz = v.capacity(); cout << "capacity changed: " << sz << '\n'; } } }
shift_to_fit缩容
释放空间由于内存管理的一些原因,不允许分段释放空间,只能整体释放,一般为异地缩容。
👉vector 动态二维数组的理解👈
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。