1.迭代器
vector实际上是由迭代器进行维护的,关于迭代器是什么,为什么要叫这个名字,后面的学习会逐渐了解,现在先将迭代器是作为指针即可。
vector底层有三个迭代器,用来起到容量、数组头、元素个数的作用。
同时为了实现方便存储各种类型的vector,vector要写成类模板。
代码语言:javascript复制template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finsh;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finsh;
}
private:
iterator _start = nullptr;
iterator _finsh = nullptr;
iterator _endofstorage = nullptr;
};
2.push_back
尾插一个元素,和顺序表一样,也要考虑扩容问题,由此也可以直接写一个reserve来进行扩容。
reserve
和顺序表判断容量满了的条件不一样,要利用迭代器来进行判断,_endofstorage来表示数组最大容量的所在位置,而_finsh表示数组最后一个元素的下一个位置,当_finsh等于_endofstorage时就意味着容量满了,此时就要扩容。
而扩容也不是在原数组上面进行操作,C 没有提供扩容的函数,所以只能另开一块空间来进行拷贝,而这里就会涉及到一个关于迭代器失效的问题。
迭代器失效
什么是迭代器失效,用容易理解的话来解释就是迭代器所指向的位置没有用了,甚至越界起不到调用数据的效果了就叫迭代器失效。
在扩容后,我们会用一个新指针来指向新开辟的空间,之后再赋给_start,但此时_finsh和_endofstorage也要进行操作,因为它们原来指向的地方被回收了,如果不修改的话会造成对野指针的解引用,因此要进行相应的修改。
代码语言:javascript复制size_t size() const
{
return _finsh - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
size_t oldsize = size();
memcpy(tmp, _start, sizeof(T) * size());
delete[] _start;
_start = tmp;
_finsh = tmp oldsize;
_endofstorage = tmp n;
}
}
完成扩容操作后我们就可以进行尾插了
代码语言:javascript复制void push_back(const T& val)
{
if (_finsh == _endofstorage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finsh = val;
_finsh ;
}
3.遍历
1.迭代器
vector提供了通过迭代器来访问每一个数组中的元素,而我们就可以利用范围for去遍历整个数组,因为范围for底层调用的也是迭代器。
2.下标方括号重载
代码语言:javascript复制T& operator[](size_t pos) const
{
assert(pos < size());
return *(_start pos);
}