vector是表示可变大小数组的序列容器
string和vector的区别:vector不支持比较大小 vector不支持+=.+等操作 string结尾有'/0'
find函数
vector函数不提供find函数,因为string要查找字符串,而vector只查找单个字符,所以干脆就不提供find函数了,但是算法库里有find函数,头文件是algorithm,如果找到就返回找到值的位置,如果没找到就返回end位置
基本变量函数
size_t size()const
{
return _finish - _start;
}
size_t capacity()const
{
return _endofstorage - _start;
}
reserve函数
我们先看一段错误代码
void reserve(size_t n)
{
if (n>capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, sizeof(T) * size());
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n;
}
}
要注意size要提前算好,如果等到算_finish时再去算size会导致size求的是新开辟空间(_start被更新了)的size导致错误
但是如果vector里的是string类型的会发生崩溃,主要原因就是memcpy是一个浅拷贝,如图
所以不能用memcpy,进行深拷贝要调用赋值重载进行深拷贝
void reserve(size_t n)
{
if (n>capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * size());
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n;
}
}
push_back函数
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
【】重载
T& operator[](size_t i)
{
assert(i<size());
return _start[i];
}
T& operator[](const size_t i)const
{
assert(i < size());
return _start[i];
}
resize函数
void resize(size_t n,const T& val=T())
{
if (n < size())
{
_finish = _start+n;
}
else
{
if (n > capacity())
{
reserve(n);
}
while (_finish != _start+n)
{
*_finish = val;
++_finish;
}
}
}
拷贝构造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
while (first != last)
{
push_back(*first);
++first;
}
}
void swap(vector<T>& v)const
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
vector<T> tmp(v.begin(),v.end());
swap(tmp);
}
拷贝构造现代写法和string的拷贝构造类似,但是string有自己的构造函数能让字符串去代工进行拷贝,但是vector只有一个无参的构造函数,所以我们写了一个迭代器版本的构造函数
赋值重载
//v1=v2
vector<T>& operator=(const vector<T> v)
{
swap(v);
return *this;
}
insert函数
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _endofstorage)
{
//扩容会导致pos失效,扩容需要更新一下pos
size_t len = pos- _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iterator end = _finish;
while (end >= pos)
{
*(end) = *(end-1);
--end;
}
*pos=x;
++_finish;
return pos;
}
vector<int>::iterator pos = find(v.begin(), v.end(), 2);
if (pos != v.end())
{
v.insert(pos, 20);
}
insert函数会有迭代器失效的问题,主要原因是因为在插入过程中如果增容,会导致pos还在原空间上的位置被释放引发野指针问题,而增容是会开辟新空间的,导致pos找不到,所以我们要更新一下pos。那么我们在传pos位置能不能传引用呢?答案是不能,我们采用传pos位置的返回值来返回,总而言之,insert可能会发生迭代器失效问题,主要看是否发生扩容问题
erase函数
当要求删除所有偶数时
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos <= _finish);
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
return pos;
}
如果我们想向上面那种代码一样写,会有以上三种情况,原因是当erase完它是返回下一个位置的下标,但是你erase完后又去往后走会导致有的数被跳过了,所以我们应该向下面这样写
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.erase(it);
}
else
{
++it;
}
}