目录
1.vector的模拟实现
2.迭代器失效问题
3.总结
1.vector的模拟实现
这里,我们使用三个指针来控制vector。
用_start指向头,_finish指向最后一个元素的尾巴,_end指向最大容量。
#include<iostream>
#include<cassert>
using namespace std;
template<class T>
class Vector
{
public:
typedef T* iterator; //将原生指针模拟成迭代器
typedef const T* const_iterator; //给一个const版本
Vector()
:_start(nullptr)
, _finish(nullptr)
, _end(nullptr)
{}
Vector(int n, const T& t = T())
:_start(nullptr)
, _finish(nullptr)
, _end(nullptr)
{
reverse(n); //先开n个空间
for (int i = 0; i < n; i++)
{
push_back(t); //插入这些值
}
}
template<class iterator_begin, class iterator_end>
Vector(iterator_begin begin, iterator_end end) //迭代器初始化版
:_start(nullptr)
, _finish(nullptr)
, _end(nullptr)
{
while (begin != end)
{
push_back(*begin);
begin++;
}
}
void swap(Vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end, v._end);
}
Vector(Vector<T>& v) //现代写法
:_start(nullptr)
, _finish(nullptr)
, _end(nullptr)
{
Vector<T> temp(v.begin(), v.end()); //先构造一个传入的拷贝
swap(temp); //再交换
}
Vector<T>& operator=(Vector<T> t) //现代写法
{
swap(t); //因为t是传值,会发生拷贝。
return *this;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
~Vector() //析构函数
{
delete[] _start;
_start = _finish = _end;
}
size_t capacity()
{
return _end - _start;
}
size_t size()
{
return _finish - _start;
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
void reverse(size_t n)
{
if (n > capacity())
{
int sz = size();
T* temp = new T[n];
if (_start) //保证非空
{
for (int i = 0; i < sz; i++)
{
temp[i] = _start[i];
}
delete _start;
}
_start = temp;
_finish = _start + sz;
_end = _start + n;
}
}
void resize(size_t n, const T& val)
{
if(n > capacity())
reverse(n);
if (n > size())
{
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
void pop_back()
{
assert(_finish > _start);
--_finish;
}
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _end) //满了
{
int len = pos - _start; //标记长度,不然下面找不到了。
reverse(capacity() == 0 ? 4 : capacity() * 2);//非0开出2倍的空间
pos = _start + len;
}
iterator cur = _finish-1; //因为finish会指向最后一个元素的尾部
while (cur >= pos) //从最后一个位置向后移动
{
*(cur + 1) = *cur;
--cur;
}
++_finish;
*pos = val;
return pos;
}
void push_back(const T& t)
{
iterator temp = insert(end(), t); //复用插入
}
iterator erase(iterator pos) //返回删除位置下一个的迭代器
{
assert(pos >= _start);
assert(pos <= _finish);
iterator cur = pos + 1;
while (cur < _finish)
{
*(cur - 1) = *cur; //挪动数据
++cur;
}
--_finish;
return pos;
}
T& front()
{
assert(size() > 0);
return *_start;
}
T& back()
{
assert(size() > 0);
return *(_finish - 1);
}
private:
iterator _start;
iterator _finish;
iterator _end;
};
需要注意的是:我们一般在拷贝构造或者扩容时会采用现代(托管+交换)的写法。
reverse:会新开一个大小n的空间,再将原来的的值拿过来。
拷贝构造:直接调用构造函数构造一个和传入的对象一样的实例,最后和this交换。
与传统的原地处理不同的是,这种方式会创造一个临时变量来托管处理,最后再交换。
2.迭代器失效问题
对于vector迭代器的失效主要分为两种情况:
a.扩容导致的野指针
b.因为挪动数据导致迭代器失效
以删除元素为例,删除2以后,pos就指向了3。pos指向内容发生了突然变化,这时也就发生了迭代器的失效,pos失去了作用。
3.总结
vector可以通过三个指针实现一个基本版。
要特别注意迭代器失效问题,这是和野指针问题一样的严重程序设计问题。