欢迎来到繁星的优快云。本期内容主要包括vector的实现与功能。
一、什么是vector?
vector可以说,打开了STL库的序章。
作为最常用、最普适、最普遍的一个容器,vector会出现在程序的各个角落。
而我们初期,可以理解为原先的数组,两者均为连续存储,在进行过函数重载和运算符重载后,实际上的操作也相差不大。
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> arr(10,0);
for(int i=0;i<arr.size(),i++){
arr[i]=i;
cout<<arr[i]<<endl;
}
return 0;
}
二、vector的功能/函数
vector相对于string这一最初始的封装而言,在设计更有章法和逻辑思维,不再有大量重复,或是无用的重载函数出现。
老规矩,先上文档网址
cplusplus.com/reference/vector/vector/
2.1vector的构造
vector();//无参构造
vector(size_type n,const value_type& val =value_type());//构造并初始化n个val
vector(const vector&x);//拷贝构造
vector(inputiterator first,inputiterator last);//使用迭代器进行初始化构造
这四个均为vector的构造方式,为了适应不同情况而创造出来。
2.2vector iterator的使用方式
之前提到过,iterator的出现,实现了多种结构的封装。大部分的容器,string、vector、list、map等,都可以用iterator进行遍历、构造等操作,这正是iterator的意义所在。
iterator在每个容器里的使用方式相同,减少了大量记忆成本。
begin+end,正向迭代器使用
rbegin+rend,反向迭代器使用
但需要注意的是,begin和rbegin分别取的是正向和反向的第一个元素,但end是最后一个元素的下一个位置,rend是第一个元素的前一个位置。
2.3vector的容量
vector的容量操作相比string而言简单很多,主要使用的也只有如下函数:
size();
capacity();
empty();
resize(size_t n, value_type val=value_type());
reserve(size_t n);
操作和string的操作完全相同,这里就不过多介绍了。
不过值得注意的是,vector里的数据类型可以是string、vector等等,所以resize里的val可能是各种类型的值,这需要开发者自己注意。
另,capacity的增长不一定是2倍,vs平台的增长速度是1.5倍,而G++平台的增长速度则是2倍,这取决于STL库的版本。故,不要固化地认为增长一定是2倍增长。
2.4vector的增删查改
vector的增删查改方式如下:
void push_back(value_type val);
void pop_back();
inputiterator find(inputiterator first, inputiterator last, const T& val);
iterator insert (iterator position, const value_type& val);
void insert (iterator position, size_type n, const value_type& val);
template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last);
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
reference operator[] (size_type n);
const_reference operator[] (size_type n) const;
void swap (vector& x);
push_back,pop_back,swap,erase,[ ]都是老朋友了,用法也大致相同。
find函数不是vector的成员函数,而是算法实现的,所以只包vector的头文件,将无法使用find。
insert函数的用法比较多,也显得会比较方便。
erase也支持了擦除一个数据,擦除一个区间内的数据的用法。
总体而言,vector的STL库还是令人满意的。
三、vector的迭代器失效问题
迭代器的封装使得我们可以忽略底层实现,而直接在各种容器中使用,但实际上底层仍然是指针。
很可惜的是,迭代器存在失效的情况。
扩容时
我们观察以下代码:
vector<int>v{1,2,3,4,5,6};
v.push_back(4);
v.resize(100,4);
v.insert(4,5);
v.assign(100,8);(将v替换为100个8)
每一行代码都有可能出现迭代器失效的问题。
不知是否还记得学习C语言的时候,我们用realloc的时候曾经说过,一旦连续的内存容量不够,realloc将重新找一个足够巨大的空间扩容。
而此时的迭代器还在原先的位置,自然发生了无效访问,且一定会报错(访问已释放的空间)
erase时
我们之前特别提到了,迭代器的end和rend所指代的位置都是没有数据的。
所以当我们消除的是最后一个元素的时候,pos的位置变成了end(因为erase的时候end也要前移),迭代器失效。
如何解决这个问题?
在VS中,面对任何可能出现迭代器失效的情况,都会直接暴力报错,这有效地避免了我们的误操作。
至于如何解决这个问题,我们只需要在迭代器失效时,及时更新迭代器即可。(重新赋值)
四、vector的模拟实现
切勿当作源码看,如需源码,详情还请看《STL源码剖析》
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
namespace show {
template <class T>
class vector {
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
iterator begin() {
return _start;
}
iterator end() {
return _finish;
}
const_iterator cbegin() {
return _start;
}
const_iterator cend() const {
return _finish;
}
// construct and destroy
vector()=default;
vector(int n, const T& value = T()) {
reserve(n);
for (int i = 0; i < n; i++) {
push_back(value);
}
}
template<class InputIterator>
vector(InputIterator first, InputIterator last) {
while (first != last) {
push_back(*first);
first++;
}
}
vector(const vector<T>& v) {
reserve(v.size());
for (auto e : v) {
push_back(e);
}
}
vector<T>& operator= (vector<T> v) {
swap(v);
return *this;
}
~vector() {
if (_start) {
delete[] _start;
_start = _finish = _endOfStorage = 0;
}
}
// capacity
size_t size() const {
return _finish - _start;
}
size_t capacity() const {
return _endOfStorage - _start;
}
void reserve(size_t n) {
if (n > capacity()) {
size_t old_size = size();
T* tmp = T[n];
for (size_t i = 0; i < old_size; i++)
{
tmp[i] = _start[i];
}
_start = tmp;
_finish = tmp + old_size;
_endOfStorage = tmp + n;
}
}
void resize(size_t n, const T& value = T()) {
if (n < size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish < _start + n)
{
*_finish = value;
++_finish;
}
}
}
///access///
T& operator[](size_t pos) {
return _start[pos];
}
const T& operator[](size_t pos)const {
return _start[pos];
}
///modify/
void push_back(const T& x) {
if (_finish = _endOfStorage) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
void pop_back() {
--_finish;
}
void swap(vector<T>& v) {
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endOfStorage, v.endOfStorage);
}
iterator insert(iterator pos, const T& x) {
if (_finish == _end_of_storage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
iterator erase(iterator pos) {
iterator it = pos + 1;
while (it != end())
{
*(it - 1) = *it;
++it;
}
--_finish;
}
private:
iterator _start=nullptr; // 指向数据块的开始
iterator _finish=nullptr; // 指向有效数据的尾
iterator _endOfStorage=nullptr; // 指向存储容量的尾
};
}
本篇内容到此结束,谢谢大家的观看!
觉得写的还不错的可以点点关注,收藏和赞,一键三连。
我们下期再见~
往期栏目: