目录
3.1size和capacity 、resize和reverese
1.vector简单介绍
上文我们介绍类字符类容器string,这次我们介绍一下它老弟vector
1.vector是一个泛型的顺序表容器。也就是说vector的空间是连续的。
2.vector存储的必须是相同类型的元素。当我们在一个vector里装入的是char的时候,就成了字符数组。
3.就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
4.本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小。为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
5.vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
6.与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。
2.vector类内函数
2.1构造函数
1.vector() 无参构造
vector <int> s1;
2.vector(size_type n, const value_type& val = value_type())构造并初始化n个val
vector <int> s2(10,3);
3、vector (const vector& x); 拷贝构造
vector <int> s2(10,3) vector <int> s3(s2); //进行拷贝构造
4.vector (InputIterator first, InputIterator last); 使用迭代器进行初始化构造
vector <int> s2(10,3); vector <int> s3(s2.begin(), s2.end()); //通过迭代器获得s2的数据
这几个拷贝构造函数形式在各位眼中已经是老演员了,没啥新鲜感。
补充:vector容器不能再向string一样直接使用cout直接进行屏幕打印,因为vector可以看做一个数组,想要遍历全部内容就需要其他方式了。
2.2vector遍历
2.21 operate+[]
这种遍历方式就是我们熟悉的下标+[]。
void test1() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); for (int i = 0; i < v1.size(); i++) { cout << v1[i] << " "; //1 2 3 4 } cout << endl; vector<int>v2(5, 7); for (int i = 0; i < v2.size(); i++) { cout << v2[i] << endl; //7 7 7 7 7 } }
2.22迭代器遍历
用迭代器进行遍历对我们来说已经见怪不怪了,接下来我们试一试vector如何遍历
反向迭代器用法还是一样的
void test1() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); vector<int>::reverse_iterator it1 = v1.rbegin(); while (it1 != v1.rend()) { cout << *it1 << " ";; //4 3 2 1 it1++; } }
2.23范围for
这个知识在string就已经出现了,接下来重点介绍一下。
让我们看一下这个代码
void test3() { string strs[] = { "int", "char", "double", "float" }; for (const auto& e : strs) { cout << e << " "; } cout << endl; system("pause"); } int main() { test3(); }
auto :推演出范围变量的数据类型。 (当我们看到auto是就可以知道这是范围for了)
e :e是范围变量的名称,该变量在循环迭代期间接受不同数组元素的值,即在第一次循环时,将数组的第一个元素拷贝给e,此时e的值是int;第二次循环时将数组的第二个元素拷贝给e,此时e的值是char;以此类推。
strs:要循环处理的数组的名称。
————————————————————————————————————————
1.为啥加引用
接下来说一说const auto& e,这个不需要&也能用
范围for在遍历的时候,把数组的每一个元素的值拷贝给e,拷贝就要开辟空间,一定程度上降低了效率,所以加引用后可以节省空间,提高效率;而且若想要修改数组的内容,不加引用就无法修改,因为拷贝的值只是数组的一个副本,而加了引用之后,就是变量的别名,也就是变量本身,就可以进行修改操作。
2.为啥要加const?
为了auto推断出来的数据类型是对常量的引用,而且for循环中不会进行写操作,加上const更好一些。
3.在遍历的时候,是如何控制循环次数的?
范围for在循环处理数组时,可以自动知道数组中的元素个数,所以不需要使用变量控制条件控制其迭代次数,也不必担心数组下标越界问题,同时可以自动把数组中的每一个元素迭代一次,所以不需要再通过下标的自增去访问数组中的元素。
3.vector容量增长
这个跟string大差不差,就是没有length了。
3.1size和capacity 、resize和reverese
Size
size指目前容器中实际有多少元素,对应的resize(size_type)会在容器尾添加或删除一些元素,来调整容器中实际的内容,使容器达到指定的大小。
Capacity指最少要多少元素才会使其容量重新分配,对应reserve(size_type new_size)会设置这个capacity值,使它不小于所指定的new_size。
所以用reserve(size_type)只是扩大capacity值,这些内存空间可能还是“野”的,如果此时使用“[ ]”来访问,则可能会越界。而resize(size_t
size:返回矢量中的元素数。(这是矢量中保存的实际对象数,不一定等于其存储容量)
size_type size() const;
capacity:能力。返回当前为矢量分配的存储空间的大小,以元素表示。
size_type capacity() const;
此容量不一定等于矢量大小。它可以相等或更大,额外的空间允许容纳增长,而无需在每次插入时重新分配。
请注意,此容量不会对矢量的大小有限制。当此容量耗尽并且需要更多容量时,容器会自动扩展它(重新分配存储空间)。向量大小的理论极限由成员max_size给出。resize:调整大小。调整容器的大小,使其包含 n 个元素。
void resize (size_type n, value_type val = value_type());
1.如果 n 小于当前容器大小,则内容将减少到其前 n 个元素,删除超出的元素(并销毁它们)。
2.如果 n 大于当前容器大小,则通过在末尾插入所需数量的元素以达到 n 的大小来扩展内容。如果指定了 val,则新元素将初始化为 val 的副本,否则,它们将被值初始化。
3.如果 n 也大于当前容器容量,则会自动重新分配分配分配的存储空间。
容器的容量和容器大小不一样,一个杯子我装半杯水,这个容器大小现在是半杯水,我要装一杯半,这个大小就大于杯子的容量了,就要换杯子重开了。
reverse:保留 请求更改容量(和capacity对应)
void reserve (size_type n);
1.请求矢量容量至少足以包含 n 个元素。
2.如果 n 大于当前矢量容量,则该函数会导致容器重新分配其存储,将其容量增加到 n(或更大)。
3.在所有其他情况下,函数调用不会导致重新分配,并且矢量容量不受影响。
4.此函数对矢量大小没有影响,也无法更改其元素。void test4() { vector<int> v1(7, 4); cout << v1.size() << endl; cout << v1.capacity() << endl; v1.reserve(50); // n是修改容器大小的 cout << v1.capacity() << endl;//50 //如果n < 当前容量大小,不做出任何改动 v1.reserve(20); //当前容器大小是50,20<50 cout << v1.capacity() << endl;//50 }
resize有点不太好理解,我们单独介绍一下。
void test5() { vector<int> v(7, 5); cout << v.size() << endl;//7 cout << v.capacity() << endl;//7 for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl; v.resize(5); // n<size,size超出的部分被丢弃 for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl; //如果n的大小 > size和capacity,更新到n。超出的部分用5初始化 v.resize(100, 5); cout << v.size() << endl;//100 cout << v.capacity() << endl;//100 //如果n的大小 < size,更新size到n,容量capacity不变 v.resize(30); cout << v.size() << endl;//30 cout << v.capacity() << endl;//100 //如果n的大小 > size,且 < capacity,更新size到n,容量capacity不变 v.resize(67); cout << v.size() << endl;//67 cout << v.capacity() << endl;//100 } int main() { test5(); }
刚开始size1和capacity都是7,用了resize(100,5)我们用resize将大小从7扩大到100,超出的部分用5进行初始化。现在size是100.
我们又感觉size有点大了,resize(30),30<100,所以size的大小由100缩小至30。若是resize开辟的大小小于capacity,那么capacity不改变。
3.2max_size
max_size返回容器容纳的最大最大元素数。
size_type max_size() const;
void test() { vector<int> v1; cout << v1.max_size() << endl;//1073741823 vector<char> v2; cout << v2.max_size() << endl;//2147483647 }
4.增删查改
vector容器相当于顺序表,它的空间是连续的。而list容器相当于链表。
4.1尾插 、尾删
push_back:在矢量末尾的当前最后一个元素之后添加新元素。
void push_back (const value_type& val);
pop_back:删除最后一个元素
void pop_back();
void test6() { vector<int> v(5,3); //3 3 3 3 3 v.push_back(1); //3 3 3 3 3 1 v.push_back(6); v.push_back(10); //3 3 3 3 3 1 6 10 for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl; v.pop_back(); //3 3 3 3 3 1 6 v.pop_back(); //3 3 3 3 3 1 for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl; } int main() { test6(); }
4.2insert,erase
insert:插入元素(一般在pos位置插入val应用最广泛)
iterator insert (iterator position, const value_type& val);
- 通过在指定位置的元素之前插入新元素来扩展矢量,从而有效地通过插入的元素数来增加容器大小。
- 这会导致自动重新分配分配的存储空间,当且仅当新矢量大小超过当前矢量容量时。
- 由于向量使用数组作为其基础存储,因此在向量端以外的位置插入元素会导致容器将所有位于位置之后的元素重新定位到其新位置。与其他类型的序列容器(如列表或forward_list)为相同操作执行的操作相比,这通常是一个低效的操作。
- 这些参数确定插入了多少个元素以及将它们初始化为哪些值。
erase:删除元素 从向量中删除单个元素(位置)或一系列元素([第一个,最后一个))。
iterator erase (iterator position); iterator erase (iterator first, iterator last);
- 这有效地减少了容器尺寸,减少了被破坏的元素的数量。
- 由于矢量使用数组作为其基础存储,因此擦除矢量端以外的位置的元素会导致容器在擦除段后将所有元素重新定位到其新位置。与其他类型的序列容器(如列表或forward_list)为相同操作执行的操作相比,这通常是一个低效的操作。
void test7() { vector<int> v; v.push_back(1); v.push_back(5); v.push_back(10); for(int i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl; //insert v.insert(v.begin(), 0); //在下标为0的位置插入0 v.insert(v.begin(), 2, 3);//在下标为0的位置往后插入两个3 for (auto e : v) cout << e << " "; //3 3 0 1 5 10 cout << endl; v.insert(v.begin() + 3, 2);//在下标为3的位置插入2 for (const auto& e : v) cout << e << " "; //3 3 0 2 1 5 10 cout << endl; cout << "----------------erase-----------------" << endl; //erase v.erase(v.begin()); //头删 for (auto e : v) cout << e << " "; //3 0 2 1 5 10 cout << endl; v.erase(v.begin() + 3); //删除下标为3的值 for (auto e : v) cout << e << " "; //3 0 2 5 10 cout << endl; //删除在该迭代器区间内的元素(左闭右开) v.erase(v.begin(), v.begin() + 3);//删除下标[0, 3)左闭右开的值 for (auto e : v) cout << e << " ";//5 10 }