程序员成长之旅——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); //使用迭代器进行初始化构造
vector iterator的使用
iterator的使用
begin() //获取第一个数据位置的iterator
end() //获取最后一个数据的下一个位置的iterator
rbegin() //获取最后一个数据位置的reverse_iterator
rend() //获取第一个数据前一个位置的reverse_iterator
cbegin() //获取第一个数据位置的const_iterator
cend() //获取最后一个数据的下一个位置的const_iterator
vector增删查改
void push_back(const value_type& val); //尾插
void pop_back(); //尾删
Inputlterator find(InputIterator first,InputIterator last,const T& val); //查找
iterator insert(iterator position,const value_type& val); //在position之前插入val
iterator erase(iterator position) //删除position位置的数据
void swap(vector& x); //交换两个vector的数据空间
reference operator[](size_type n); //像数组一样访问
vector常见问题
vector的底层实现原理以及实现机制
vector简介
关于vector简单的说的话就是一个动态增长的数组,里面有一个指针指向一片连续的内存空间,当空间装不下要容纳的数据的时候会自动申请一片更大的空间(空间配置器)将原来的数据拷贝到新的空间,然后就会释放旧的空间。当删除的时候空间并不会释放,只是清空了数据。
vector和数组的区别
vector的数据安排以及操作方式与数组非常相似,两者唯一区别在于空间运用的灵活性,数组是静态空间一旦配置了就不能在改变大小,如果要增容的话,就要把数据搬到新的数组里面,然后再把原来的空间释放掉还给操作系统。vector是动态的随着元素的增加,它的内部机制会自动的扩充空间来容纳新的元素。因此,vector的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们不必害怕空间不足而一开始开辟一块很大的内存。
vector实现机理
vector的实现技术,关键在于对大小的控制以及重新配置时的数据移动效率。一旦vector的旧空间满载了,如果客户端每新增加一个元素,vector的内部只是扩充了一个元素空间,其实这样是比较不明智的。因为所谓的扩充空间(无论多大),过程都是配置新空间----数据移动----释放旧空间,成本还是比较高的。vector维护是一个连续的线性空间,所以vector支持随机访问。
vector容易犯的错误
在vector的动态增加大小的时候,并不是在原有的空间上持续增加新的空间(无法保证原空间的后面还有可供配置的空间),而是以原大小的两倍另外配置一块较大的空间,然后将原来的内容拷贝过来,并释放原来的空间。因此,对vector的任何操作一旦引起了空间的重新配置,指向原vector的所有迭代器会都失效了,这是比较容易犯的一个错误。
vector空间增长问题
size() //获取数据个数
capacity() //获取容量大小
empty() //判断是否为空
void resize(size_type n,value_type val = value_type()); //改变vector的size
void reserve(size_type n); //改变vector放入的capacity
- capacity的代码在vs和g++下分别运行会发现,vs下的capacity是按1.5倍增长的,g++是按2位增长的。因此它具体的增长多少是根据具体的需求定义的。
- reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
- resize在开空间的同时还会进行初始化,影响size
vector< char >和string的区别
vector<char> vch;
vch.push_back('\0');
cout<<vch.size()<<endl;//输出1
string s3="";
cout<<s3.size()<<endl;//输出0
string s4="\0";
cout<<s4.size()<<endl;//输出0
string类型会默认结尾是一个空字符,如果一个string只含有一个空字符,则该string为空。vector则不同,含有一个空字符后,就不在是空的。总的来说:两种类型不同,还有就是string是以‘\0’结尾,vector不是。
vector迭代器失效问题
由于迭代器的类型是:
typedef T* iterator
它是一个指针。
//insert/erase导致的迭代器失效
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
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; // 此处会导致非法访问
// 在pos位置插入数据,导致pos迭代器失效。
// insert会导致迭代器失效,是因为insert可
// 能会导致增容,增容后pos还指向原来的空间,而原来的空间已经释放了。
pos = find(v.begin(), v.end(), 3);
v.insert(pos, 30);
cout << *pos << endl; // 此处会导致非法访问
return 0;
}
// 常见的迭代器失效的场景
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int a[] = { 1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 实现删除v中的所有偶数
// 下面的程序会崩溃掉,如果是偶数,erase导致it失效
// 对失效的迭代器进行++it,会导致程序崩溃
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
v.erase(it);
++it;
}
// 以上程序要改成下面这样,erase会返回删除位置的下一个位置
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
it = v.erase(it);
else
++it;
}
return 0;
}
迭代器失效:
auto it = v.begin(); it实际已经指向vector底层空间的起始位置
v.push_back(data);
通过it迭代器访问vector中的元素—>可能会引起代码崩溃
1.如果底层空间的改变—push_back/insert resize/reserve swap assign
2.erase(pos)–pos的迭代器失效 pos–>vector中的某个位置–>T类型对象
解决迭代器失效: 给迭代器重新赋值