vector是STL(标准模板库)中最常见的容器,它是一种顺序容器,支持随机访问。vector是一块连续分配的内存,从数据安排的角度来讲,和数组极其相似,不同的地方就是:数组是静态分配空间,一旦分配了空间的大小,就不可再改变了;而vector是动态分配空间,随着元素的不断插入,它会按照自身的一套机制不断扩充自身的容量。
vector的扩充机制:按照容器现在容量的一倍进行增长。vector容器分配的是一块连续的内存空间,每次容器的增长,并不是在原有连续的内存空间后再进行简单的叠加,而是重新申请一块更大的新内存,并把现有容器中的元素逐个复制过去,然后销毁旧的内存。这时原有指向旧内存空间的迭代器已经失效,所以当操作容器时,迭代器要及时更新。
vector() 无参构造
vector(size_type n,
const value_type& val = value_type()) 构造并初始化n个val
vector (const vector& x); 拷贝构造
vector (InputIterator first,
InputIterator last); 使用迭代器进行初始化构造
#include
#include
using namespace std;
int main() {
vector<int> sdt;//无参构造
vector<int> s1(4,100);//构造并初始化4个100
vector<int> s2(s1.begin(), s1.end());//使用迭代器进行初始化构造
vector<int> s3(s1);//拷贝构造
return 0;
}
把s1的内容拷贝给s2和s3它们的有效位为4位
std采用无参构造所以没有数据
begin + end 获取第一个数据位置的iterator/const_iterator,
获取最后一个数据的下一个位置的
iterator/const_iterator
rbegin + rend 获取最后一个数据位置的reverse_iterator,、
获取第一个数据前一个位置的reverse_iterator
#include
#include
using namespace std;
int main() {
vector<int> sdt;//无参构造
vector<int> s1(2,4);//构造并初始化4个100
//vector s2(s1.begin(), s1.end());//使用迭代器进行初始化构造
//vector s3(s1);//拷贝构造
// const对象使用const迭代器进行遍历打印
s1.push_back(1);
s1.push_back(2);
s1.push_back(3);
s1.push_back(4);
//使用迭代器进行遍历打印
vector<int>::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//反向迭代器进行遍历打印
vector<int>::reverse_iterator it1 = s1.rbegin();
while (it1 != s1.rend())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
return 0;
}
这里的push_back函数功能和string中的功能一样都是在尾部插入一个数据
size 获取数据个数
capacity 获取容量大小
empty 判断是否为空
resize 改变vector的size
reserve 改变vector放入capacity
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
这个问题经常会考察,不要固化的认为,顺序表增容都是2倍,具体增长多少是根据具体的需求定义
的。vs是PJ版本STL,g++是SGI版本STL。
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问
题。
resize在开空间的同时还会进行初始化,影响size
// vector::capacity
#include
#include
int main()
{
size_t sz;
std::vector<int> foo;
sz = foo.capacity();
std::cout << "making foo grow:\n";
for (int i = 0; i < 100; ++i) {
foo.push_back(i);
if (sz != foo.capacity()) {
sz = foo.capacity();
std::cout << "capacity changed: " << sz << '\n';
}
}
}
#include
#include
using namespace std;
int main() {
vector<int> sdt;//无参构造
cout <<"1为空:"<<sdt.empty() << endl;//判断sdt是否为空
vector<int> s1(1, 1);//构造并初始化4个100
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.push_back(1);
s1.push_back(2);
s1.push_back(3);
s1.push_back(4);
//size 获取数据个数
// capacity 获取容量大小
// empty 判断是否为空
// resize 改变vector的size
// reserve 改变vector放入capacity
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.resize(10);//把s1有效位扩至10没数据的用0代替
cout << "把s1有效位扩至10没数据的用0代替" << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
vector<int>::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
cout << "把s1有效位扩至15没数据的用5代替" << endl;
s1.resize(15,5);//把s1有效位扩至15没数据的用5代替
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << "//把s1的空间长度扩至100" << endl;
s1.reserve(100); //把s1的空间长度扩至10
cout << s1.size() << endl;
cout << s1.capacity() << endl;
vector<int>::iterator it1 = s1.begin();
while (it1!= s1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
return 0;
}
push_back 尾插
pop_back 尾删
find 查找。 (注意这个是算法模块实现,不是vector的成员接口)
insert 在position之前插入val
erase 删除position位置的数据
swap 交换两个vector的数据空间
operator[] 像数组一样访问
#include
#include
using namespace std;
int main() {
vector<int> sdt;//无参构造
vector<int> s1(1, 1);//构造并初始化4个100
s1.push_back(1);
s1.push_back(2);
s1.push_back(3);
s1.push_back(4);
vector<int>::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it<< " ";
++it;
}
cout << endl;
s1.pop_back();
vector<int>::iterator it1 = s1.begin();
while (it1 != s1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(s1.begin(), s1.end(), 3);
// 在pos位置之前插入44
s1.insert(pos, 44);
vector<int>::iterator it2 = s1.begin();
while (it2 != s1.end()) {
cout << *it2 << " ";
++it2;
}
cout << endl;
//找到3的位置
vector<int>::iterator pos1= find(s1.begin(), s1.end(), 3);
//删除之前插入到之前的44
s1.erase(pos1 -1);
vector<int>::iterator it3 = s1.begin();
while (it3 != s1.end()) {
cout << *it3 << " ";
++it3;
}
return 0;
}
#include
#include
using namespace std;
int main() {
vector<int> sdt;//无参构造
vector<int> s1(1, 1);//构造并初始化4个100
s1.push_back(1);
s1.push_back(2);
s1.push_back(3);
s1.push_back(4);
cout << s1[0] << endl;
s1[0] = 4;//通过[]改变第1个值
cout << s1[0] << endl;
//通过[]遍历
for (int i = 0; i < s1.size(); i++) {
cout << s1[i] <<" ";
}
cout << endl;
//s1首部插入4
s1.insert(s1.begin(), 4);
for (int i = 0; i < s1.size(); i++) {
cout << s1[i] << " ";
}
cout << endl;
sdt.swap(s1);
cout << "sdt:";
for (int i = 0; i < sdt.size(); i++) {
cout << sdt[i] << " ";
}
cout << endl;
cout << "s1:";
for (int i = 0; i < s1.size(); i++) {
cout << s1[i] << " ";
}
cout << endl;
// 范围for
for (auto x: sdt) {
cout << x << " ";
}
cout << endl; return 0;
}
标记的地方 交换前sdt为空 s1和sdt换了工作空间s1为空
#include
#include
using namespace std;
int main() {
vector<int> sdt;//无参构造
vector<int> s1(1, 1);//构造并初始化4个100
s1.push_back(1);
s1.push_back(2);
s1.push_back(3);
s1.push_back(4);
vector<int>::iterator it = s1.begin();
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(s1.begin(), s1.end(), 3);
// 在pos位置之前插入44
s1.insert(pos, 44);
vector<int>::iterator it2 = s1.begin();
while (it2 != s1.end()) {
cout << *it2 << " ";
++it2;
}
cout << endl;
//删除之前插入到之前的44
s1.erase(pos - 1);
return 0;
}
说明:s1.insert(pos, 44);在插入是会因为空间不够会增容,增容之后pos会失效,之后的s1.erase(pos - 1);操作会出错,所以整个程序会奔溃。
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。*
**对于vector可能会导致其迭代器失效的操作有:
#include
using namespace std;
#include
int main()
{
vector<int> v{
1,2,3,4,5,6};
auto it = v.begin();
// 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
// v.resize(100, 8);
// reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变
// v.reserve(100);
// 插入元素期间,可能会引起扩容,而导致原空间被释放
// v.insert(v.begin(), 0);
// v.push_back(8);
// 给vector重新赋值,可能会引起底层容量改变
v.assign(100, 8);
/*
*/
while(it != v.end())
{
cout<< *it << " " ;
++it;
}
cout<<endl;
return 0;
}
说明: 出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。
解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。
下边代码重新赋值就解决了迭代器失效的问题
#include
#include
using namespace std;
int main() {
vector<int> sdt;//无参构造
vector<int> s1(1, 1);//构造并初始化4个100
s1.push_back(1);
s1.push_back(2);
s1.push_back(3);
s1.push_back(4);
vector<int>::iterator it = s1.begin();
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(s1.begin(), s1.end(), 3);
// 在pos位置之前插入44
s1.insert(pos, 44);
vector<int>::iterator it2 = s1.begin();
while (it2 != s1.end()) {
cout << *it2 << " ";
++it2;
}
cout << endl;
pos迭代器失效要重新找到3的位置
找到3的位置
vector<int>::iterator pos1 = find(s1.begin(), s1.end(), 3);
//修改地方
//删除之前插入到之前的44
s1.erase(pos1 - 1);
vector<int>::iterator it3 = s1.begin();
while (it3 != s1.end()) {
cout << *it3 << " ";
++it3;
}
return 0;
}
#include
using namespace std;
#include
int main()
{
int a[] = {
1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
// 删除pos位置的数据,导致pos迭代器失效。
v.erase(pos);
cout << *pos << endl; // 此处会导致非法访问
return 0;
}
erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代
器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是
没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效
了。
以下代码的功能是删除vector中所有的偶数
#include
using namespace std;
#include
int main()
{
vector<int> v{
1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
v.erase(it);
++it;
}
return 0;
}
这个程序删除vector中的偶数依次遍历当删除2后it会指向3自增指向4但是删除4后会向后挪移而4已经是end会访问不到而出错,而且要是两个偶数放在一起会跳过第二个偶数
int main()
{
vector<int> v{
1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
it = v.erase(it);
else
++it;
}
return 0;
}
这是改良版的删除之后把挪动的位置位置赋给it,而且这个程序也解决了删除后还自增的问题,两个偶数放在一起还会被删除