vector
1.vector是一个顺序容器,是数组的升级版,可以改变大小,即动态增长的数组。
2.vector实质上就是将数组和方法封装的一个类。
vector的底层
1.vector的迭代器
(1)vector的迭代器就是一个原生指针,若数据类型为T,则其迭代器就是T*的指针。
(2)将T*重定义为iterator(即typedef T* iterator)。
2.vector的成员变量为三个指针:start、finish、endofstorage。
(1)start:表示有效数据的起始位置
finish:表示最后一个有效数据的下一个位置
endofstorage:表示当前有效空间的下一个位置
(2)因为vector的迭代器就是原生指针,所以利用迭代器,此时其三个成员变量就有如下定义:
iterator start; //T* start
iterator finish; //T* finish
iterator endofstorage; //T* endofstorage
3.vector内部通过一个expand( )函数进行扩容(该函数我们无法调用,但我们能通过resize和reserve进行扩容)
(1)当finish == endofstorage时需要扩容,一般扩大为原来容量的2倍。(扩大的倍数是可以改变的,但是一般不要太大,否则空间会浪费很多,但是也不能太小,否则就会频繁的扩容);
(2)扩容时,会开一段新的空间再把以前的数据拷下来(这里也会存在深浅拷贝);
(3)再释放旧的空间,然后再将新空间给我。
vector的初始化方式
vector<int> v1; //v1是一个空的vector
vector<int> v2 = v1; //v2是v1拷贝构造的
vector<int> v3(v1); //v3也是v1拷贝构造的
vector<int> v4 = { 1, 2, 3, 4 }; //v4包含4个元素
vector<int> v5(10); //v5包含10个元素,每个元素的数值为0
vector<int> v6(10, 2); //v6包含10个元素,每个元素的数值为2
vector<int> v7{ 1, 2, 3, 4 }; //与v4的内容一致,包含4个元素
//采用列表初始化,则花括号里面的类型必须与vector给定的类型一致,如果不一致则不是列表初始化,编译器会尽量想办法按照类型初始化,如果不能就会出错
vector<string> v8{ 10 }; //因为vector里面的类型为string,而{}里面是int类型的数据,则不是初始化列表,而是v8里面含有10个空字符串
vector<string> v9{ 10, "11" }; //v9里面包含10个字符串“11”
vector的resize和reserve
- resize和reserve都可以用来扩容,但是他们之间会有一些区别
1.resize
(1)用来改变size(size表示有效数据的个数)
void resize (size_type n, value_type val = value_type());
(2)用法:
①当n大于以前的size,则会将capacity和size都会增加至n
int main()
{
vector<int> v = { 1, 2, 3, 4 };
v.resize(10); //此时v的size和capacity都会变为10,并且后面的数据默认初始化为0,即v里面的数据为1 2 3 4 0 0 0 0 0 0
system("pause");
return 0;
}
②如果n小于以前的size,则只会将size改变并不会改变capacity.
int main()
{
vector<int> v = { 1, 2, 3, 4 };
v.resize(2); //此时v的size是2,capacity为4,并且v里面的数据为1 2
system("pause");
return 0;
}
2.reserve
(1)用来改变容量
void reserve (size_type n);
(2)用法:
①如果n大于以前的size,只会将capacity改变,但不改变size
int main()
{
vector<int> v = { 1, 2, 3, 4 };
v.reserve(10); //此时v的size为4,capacity为10,v里面数据仍为1 2 3 4
system("pause");
return 0;
}
②如果n小于以前的size,则size和capacity都不会改变
int main()
{
vector<int> v = { 1, 2, 3, 4 };
v.reserve(2); //此时v的size和capacity都不变,仍为4,则v里面的数据仍为1 2 3 4
system("pause");
return 0;
}
3.resize和reserve的区别与联系
(1)resize和reserve都是用来开空间的函数,但是必须提前知道所开 空间的大小,两个底层调用expand函数进行扩容;
(2)resize不仅可以可以将容量扩大,还可以改变size,而且还会对数据进行初始化,如果不给初始化的值,默认按照缺省类型进行初始化;如果给定初始化的值,就会按照自己给定的值进行初始化;reserve只是将容量扩大了,但是并没有改变size,即数据个数不变,一般配合push_back使用。
(3)当n(传的参数)大于size时,无论是resize和reserve都会增加capacity;当n小于size时,无论是resize还是reserve都不改变capacity,但是resize还会将size缩减至n。
vector的相关接口
- vector位于头文件
<vector>
里,当使用vector的相关接口必须包含该头文件。 - vector不支持头上的插入和删除,因为头上的插入和删除效率太低了,需要反复挪动数据,但是通过insert和erase也可以在头上进行插入和删除。
1.push_back
(1)用于尾上插入一个数据;
void push_back (const value_type& val);
(2)使用:
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2); //这步完成后,v里面的数据为1 2
}
2.pop_back
(1)用于删除尾上的数据
void pop_back();
(2)使用:
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2); //此时v里面的数据为1 2
v.pop_back(); //此时v里面的数据为1
}
3.insert
(1)用于在pos位置前面插入数据,可以插入一个也可以插入n个还可以插入一段迭代器区间中的元素。
返回值:返回一个迭代器,该迭代器指向插入位置,或者说是插入的第一个有效数据所在位置
//1.在pos前面插入一个值
iterator insert (iterator position, const value_type& val);
//2.在pos前面插入n个值
void insert (iterator position, size_type n, const value_type& val);
//3.在pos前面插入一段迭代器区间
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
(2)使用
注意:插入位置必须在v.end()之前,可以包括v.end()
①在pos前面插入一个数
int main()
{
vector<int> v;
vector<int>::iterator it = v.begin();
v.insert(it, 10); //第一个参数为vector的迭代器,第二个参数是插入的数据的值,此时v里面就包含一个10
system("pause");
return 0;
}
②在pos前面插入n个数
int main()
{
vector<int> v;
vector<int>::iterator it = v.begin();
v.insert(it,2, 10); //此时v里面包含两个10
system("pause");
return 0;
}
③在pos前面插入一段迭代器区间(注意STL里面的迭代器区间全部都是左闭右开的区间)
- 迭代器可以是同类vector自身的迭代器,也可以是与自己定义vector的类型一样的指针(例如
vector<int>
,则迭代器区间的类型可以是int*)
例1:在v1的begin()之前插入v2的第一到第三个数,由于迭代器是左闭右开的区间,所以v1里面只插入了v2的前两个数。
int main()
{
vector<int> v1;
vector<int> v2 = { 1, 2, 3, 4 };
vector<int>::iterator it1 = v1.begin();
vector<int>::iterator it2 = v2.begin();
v1.insert(it1, it2, it2 + 2); //此时v1里面包含 1 2
system("pause");
return 0;
}
例2:在pos前面插入一个数组里面的某个区间
int main()
{
int a[] = { 1, 2, 4, 5, 6 };
vector<int> v1;
vector<int>::iterator it = v1.begin();
v1.insert(it, a, a + 3); //此时v里面数据为 1 2 4
return 0;
}
如果将一个double类型数组里面某个迭代器区间的值插入到一个存放int型数据的vector里面,则会将double类型转换为int再插入到vector里面。
4.erase
(1)用于删除某些元素,可以删除某个位置上的数据,也可以删除一段迭代器区间的数据
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
(2)使用:
①删除某个位置上的数据
int main()
{
vector<int> v1 = { 1, 2, 3, 4 };
vector<int>::iterator it= v1.erase(v1.begin() + 1); //此时v里面的数据变为 1 3 4
cout << *it << endl; //此时it指向3所在的位置
system("pause");
return 0;
}
注意:删除元素可能会导致迭代器失效
int main()
{
vector<int> v1 = { 1, 2, 3, 4 };
vector<int>::iterator it= v1.erase(v1.begin() + 3); //将4删除了,但是迭代器会指向4的下一个位置,但是4已经是最后一个位置,则4的洗一个位置就是v1.end(),但是这个位置不是有效位置,再想解引用it,则会发生越界行为
cout << *it << endl; //越界
system("pause");
return 0;
}
②删除迭代器区间的某些数据
int main()
{
vector<int> v1 = { 1, 2, 3, 4 };
vector<int>::iterator it= v1.erase(v1.begin(),v1.begin()+2); //此时v里面只包含 3 4,并且it指向的3所在的那个位置
system("pause");
return 0;
}
vector中访问某个有效数据的相关接口
1.operator[ ]
(1)用于访问某个位置上的数据
reference operator[] (size_type n);
const_reference operator[] (size_type n) const;
//返回值:返回当前位置数据的引用,表示可读可改,const_reference是针对const版本的,只能读不能改
(2)使用:
int main()
{
vector<int> v = { 1, 2, 3, 4 };
cout << v[0] << endl; //输出1
v[0] = 10;
cout << v[0] << endl; //将v[0]修改之后,此时输出10
system("pause");
return 0;
}
2.at
(1)at与operator[ ]的接口完全一致,都是返回引用;只是用法上稍有区别,只是operator[ ]重载了[ ],所以可以像数组一样访问。
reference at (size_type n);
const_reference at (size_type n) const;
(2)使用:
int main()
{
vector<int> v = { 1, 2, 3, 4 };
cout << v.at(0) << endl; //输出1
v.at(0) = 10;
cout << v.at(0) << endl; //输出10
system("pause");
return 0;
}
(3)operator[ ] 与at的区别
operator[ ] 如果越界访问,会出现断言错误;而at是抛异常,且会调用abort函数,使得自己异常终止。
3.front
(1)用于:取第一个数据
reference front();
const_reference front() const;
(2)使用:
int main()
{
vector<int> v = { 1, 2, 3, 4 };
cout << v.front() << endl; //输出 1
system("pause");
return 0;
}
4.back
(1)用于:取最后一个数据
reference back();
const_reference back() const;
(2)使用:
int main()
{
vector<int> v = { 1, 2, 3, 4 };
cout << v.back() << endl; //输出 4
system("pause");
return 0;
}
遍历vector的两种方式
1.通过for循环
int main()
{
vector<int> v = { 1, 2, 3, 4 };
for (size_t i = 0; i < v.size(); ++i)
{
cout << v[i] << " ";
}
system("pause");
return 0;
}
2.通过迭代器
int main()
{
vector<int> v = { 1, 2, 3, 4 };
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
system("pause");
return 0;
}