一、vector的点
1. [] 和 at() 的越界访问
vector<int> v1{ 1,2,3,4 };
//v1[4] = 5; //越界直接结束,assert判断
try
{
v1.at(4) = 5; //抛异常
}
catch (...)
{
cout << "beyond" << endl; //输出beyond
}
2.迭代器失效问题
简单来说就是在迭代器创建后
使用了reverse,resize,insert,push_back等导致的增容
导致迭代器仍然指向已经被释放的旧空间
一个应对迭代器失效的过程
//删除容器中所有的偶数
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
v.erase(it);
}
//然后该怎么++it?
++it; //此时会报错,为什么?
//erase删除一个数据后,其他数据向前移动,移动后其实it已经指向我们想要遍历的下一个元素
//此时再++it就会导致错过部分数据
else
{
++it; //那么这样处理可以么?
//Linux环境下这样可以达到目的,但是vs编译环境自动检查且较为严格,仍然报错
}
}
//正确做法
vector<int>::iterator it2 = v.begin();
while (it2 != v.end())
{
if (*it2 % 2 == 0)
it2 = v.erase(it2); //正确做法 erase会返回删除的it的下一个位置的迭代器
else
++it2;
}
二、vector的模拟实现
有以下要点:
reserve中浅拷贝memmove问题
新型拷贝构造和重载赋值符写法
insert时的迭代器失效
namespace 9TSe
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}
//v2(v1)
//vector(const vector<T>& v)
//{
// _start = new T[v.capacity()]; //开辟新空间
// _finish = _start;
// _endofstorage = _start + capacity();
// for (size_t i = 0; i < v.size(); ++i)
// {
// *_finish = v[i];
// ++_finish;
// }
//}
//拷贝构造的另一种方法
vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
for (auto& e : v)
{
reserve(v.capacity());
push_back(e);
}
}
//vector<T>& operator=(const vector<T>& v)
//{
// if (*this != &v) //防止自己赋值给自己
// {
// delete[] _start; //s1 = s2 s1有自己的空间,先释放
// _start = new T[v.capacity()];
// memcpy(_start, v._start, sizeof(T) * v.size());
// }
// return *this;
//}
//重载赋值运算符简便写法
void swap(vector<T>& v)
{
::swap(_start, v._start);
::swap(_finish, v._finish);
::swap(_endofstorage, v._endofstorage);
}
vector<T>& operator=(vector<T> v)
{
if (*this != &v)
swap(v); //这里不用库内自带的swap是因为,其会生成三个深拷贝,有很大的代价
return *this;
}
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
void reserve(size_t n)
{
if (n > capacity()) //增大容量时
{
size_t sz = size();
T* tmp = new T[n];
if (_start) //这里的用意是防止_start为空指针时扩容,会导致memcpy出错(memcpy内不能存在空指针)
{
//memcpy(tmp, _start, sizeof(T) * size()); //这个memcpy是浅拷贝,当T为string时,会导致指针指向同一位置,释放两次同一空间
for (size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = tmp + sz; //如果这里赋予 tmp + size() 会出错
//sz的计算方式是_finish-_start
//_start已经指向新空间了,finish还指向旧空间
_endofstorage = tmp + n;//这里如果赋予tmp + capacity() 也会出错,与上同理
}
}
void resize(size_t n, const T& v = T()) //这里的T()为T类型的默认缺省值,有int() double() 等都有
//int() double()等内置类型,只是专门创建了此形式的东西
//string ,vector 是调用构造函数
{
if (n < size()) //缩容
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}
while (_finish < _start + n)
{
*_finish = v;
++_finish;
}
}
}
void push_back(const T& x)
{
if (_finish == _endofstorage) //扩容
{
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);
}
*_finish = x;
++_finish;
}
void insert(iterator pos, const T& v) //这里会有迭代器失效问题
{
assert(pos <= _finish);
if (_finish == _endofstorage)//pos会扩容后失效
{
size_t n = pos - _start; //因此要记录之间的距离,以明确新pos指针指向的位置
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);
pos = _start + n; //设置新pos
}
iterator end = _finish - 1;
while (end >= pos) //移动数据
{
*(end + 1) = *end;
end--;
}
*pos = v;
++_finish;
}
iterator erase(iterator pos)
{
assert(pos < _finish&& _start != _finish);
iterator it = pos;
while (it < _finish)
{
*it = *(it + 1);
++it;
}
_finish--;
return pos;
}
size_t size()const
{
return _finish - _start;
}
size_t capacity()const
{
return _endofstorage - _start;
}
iterator end()
{
return _finish;
}
iterator begin()
{
return _start;
}
const_iterator end()const
{
return _finish;
}
const_iterator begin()const
{
return _start;
}
T& operator[](size_t i)
{
assert(i < size());
return *(_start + i);
}
const T& operator[](size_t i)const
{
assert(i < size());
return *(_start + i);
}
void pop_back()
{
assert(_start < _finish);
_finish--;
}
private:
iterator _start;
iterator _finish; //这个指针指向的是最后一个数据的下一个数据的位置
iterator _endofstorage;
};
}