C++:Vector的模拟实现

简介: C++:Vector的模拟实现

                                                 创作不易,感谢三连 !!

一,前言

      在学习string类的时候,我们可能会发现遍历的话下标访问特别香,比迭代器用的舒服,但是下标其实只能是支持连续的空间,他的使用是非常具有局限性的,随着STL学习的深入我们会发现其实迭代器才是大佬!!Vector虽然也支持下标访问,但是很多成员函数都是用的迭代器,所以我们要模拟实现的话迭代器十分重要,vs使用的是PJ版的STL版本,比较难懂,所以我们模拟实现统一用SGI版本去实现,所以在模拟实现之前,我们要去看看他的源码到底有哪些成员变量

      SGI下的vector有三个成员变量,通过观察其他源码可以大致推断  _start是指向起始位置,_finish是指向有效数据的下一个位置(迭代器都遵循左闭右开),end_of_storage是指向有效容量的最后一个位置。

    通过这个我们可以观察到SGI版本下的迭代器其实就是一个原生指针,value_type*类型相当于是模板T对应的指针类型,有了这些大致了解,我们就可以去模拟实现啦!!

二,vector的模拟实现

   大致框架需要有模板(类外定义)/迭代器以及迭代器的获取(public定义,要有可读可写的也要有可读不可写的)/成员变量(private定义)  并且为了不和库的vector冲突,我们需要自己搞一个命名空间

namespace cyx
{
//模板
template<class T>
//迭代器(可读可写)
class vector
{
public:
typedef T* iterator;
iterator begin()
{
  return _start;
}
iterator end()
{
  return _finish;
}
//迭代器(可读不可写)
typedef const T* const_iterator;
const_iterator begin() const
{
  return _start;
}
const_iterator end() const
{
  return _finish;
}
private:
//成员变量
iterator _start;
iterator _finish;
iterator _end_of_storage;
}
}

然后我们开始实现!!

2.1 相关成员函数

2.1.1 无参构造函数

//无参构造函数
  vector()
    :_start(nullptr)
    ,_finish(nullptr)
    ,_end_of_storage(nullptr)
  {}

2.1.2 迭代器区间构造

//传别人的迭代器进行构造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
  :_start(nullptr)
  , _finish(nullptr)
  , _end_of_storage(nullptr)
{
     //这里传的是别人的迭代器,不知道会传多少数据,不能提前扩容,只能让pushback的时候去判断
  while (first != last)
  {
    push_back(*first);
    ++first;
  }
}

push_back是尾插数据,具体实现后面会写。

思考:为什么迭代器也要搞个类模板呢?

       答:本质上是为了让这个函数更加灵活,可以传不同类型的迭代器来帮助我们初始化!!

比如这个地方我们传string类的迭代器

传vector类的迭代器

2.1.3 有参构造函数(对n个存储的类型去调用他们的构造)

//有参构造函数(对n个存储的类型去调用他们的构造)
vector(size_t n,const T&val=T() )
  :_start(nullptr)
  , _finish(nullptr)
  , _end_of_storage(nullptr)
{
  reserve(n);//因为我们知道会进多少数据,所以可以提前开空间
  for (int i = 0; i < n; ++i)
    push_back(val);
}

reserve是扩容到n,具体实现后面会写。

思考:

1.缺省值T( )是什么意思

     答:这个地方的缺省值不能给0!!因为vector可能会存储内置类型,也可能会存储自定义类型,比如vector<string>,所以如果我们没给值,缺省值就要给他的默认无参构造函数,这个默认构造函数可以使用匿名对象。

2.const T&val=T()  T()不是用一次就析构吗,为什么可以用引用

    答:T()是一个用一次就析构的匿名对象,其实本质上是因为他没有名字,用T引用val可以充当他的名字,此时用val就相当于用这个匿名对象,所以匿名对象的生命周期被延长到和val一样但是由于匿名对象是一个临时变量,所以具有常性,所以必须用const修饰的val才可以当他的别名,否则会出现权限放大!!

3.非法的间接寻址是为什么?

如下图我传(10,5),会出非法间接寻址

但是我传(10u,5)就可以正常使用了,为什么会这样??

 答:

根据上图写出一个重载的有参构造

//重载一个防止间接寻址
vector(int n, const T val = T())
  :_start(nullptr)
  , _finish(nullptr)
  , _end_of_storage(nullptr)
{
  reserve(n);//因为我们知道会进多少数据,所以可以提前开空间
  for (int i = 0; i < n; ++i)
    push_back(val);
}

2.1.4 assign

assign跟赋值很相似,都是将新内容分配给容器,替换其当前内容,并相应地修改其大小。然后assign有两个版本,第一个版本是迭代器区间替换(我们可以控制范围),第二个版本是直接替换成n个相同元素,而赋值的话就是简单的直接替换,可以说是assign的一个特殊情况,所以我们先实现assign再用=来复用。

(1)迭代器区间替换

//assign的迭代器区间替换
template <class InputIterator>  
void assign(InputIterator first, InputIterator last)
{
  size_t sz = last - first;
  T* temp = new T[sz];
  for (int i = 0; i < sz; ++i)
    temp[i] =*(first+i);
  delete[]_start;
  _start = temp;
  _finish = _end_of_storage = _start + sz;
}

(2)直接替换

//assign的直接替换
  void assign(size_t n, const T& val)
  {
    T* temp = new T[n];
    for (int i = 0; i < n; ++i)
      temp[i] = val;
    delete[]_start;
    _start = temp;
    _finish = _end_of_storage = _start + n;
  }

    但是这样的话,就会跟之前的有参构造一样,会由于n是size_t类型,如果两个都是传的int就会出现间接寻址的问题

所以我们必须要写一个重载版本

void assign(int n, const T& val)
  {
    T* temp = new T[n];
    for (int i = 0; i < n; ++i)
      temp[i] = val;
    delete[]_start;
    _start = temp;
    _finish = _end_of_storage = _start + n;
  }

2.1.5 拷贝构造+memcpy拷贝问题+赋值重载

但是真的有这么顺利吗??

思考:

为什么存string类就会崩了??    这就涉及到memcpy的拷贝问题

我们以上述问题来画图解释一下

总结:

1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中

2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。

     如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是
浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

 

所以在这个地方我们的拷贝构造不能用memcpy

//拷贝构造(传统)
vector(const vector<T>& v)
  :_start(nullptr)
  ,_finish(nullptr)
  ,_end_of_storage(nullptr)
{
  _start = new T[v.capacity()];
  //memcpy(_start, v._start, sizeof(T)*v.size());  不能用memcpy 是浅拷贝
  for (int i = 0; i < v.size(); ++i)
    _start[i] = v._start[i];//实现重载运算符
  _finish = _start + v.size();
  _end_of_storage = _start + v.capacity();
}

但是真的没有问题了吗??看看这个

但道理来说得打印出9个1  结果呢??

原因是什么呢,我们先看看resize函数

传统赋值重载可以复用assign

//重载赋值=(传统)
vector<T>& operator=(const vector<T>& v)
{
  assign(v.begin(), v.end());
  return *this;
}

2.1.6 拷贝构造和赋值重载的现代写法

现代的写法就是用的资本家的思想,窃取劳动成果

先写个swap函数

//交换
void swap(vector<T>& v)
{
  std::swap(_start, v._start);
  std::swap(_finish, v._finish);
  std::swap(_end_of_storage, v._end_of_storage);
}

       拷贝构造的现代写法思路:创建一个临时对象利用被拷贝对象的迭代器区间构造,然后再swap一下就可以了!

vector(const vector<T>& v)
    :_start(nullptr)
    , _finish(nullptr)
    , _end_of_storage(nullptr)
  {
    vector<T> temp(v.begin(), v.end());//让临时对象借助迭代器区间构造出来
    swap(temp);//窃取革命成果
  }

      赋值重载的现代写法的思路:反正我自己的空间也不要了,被赋值对象传值过来(这样被赋值对象不会被修改),然后直接和临时对象swap就可以了!!

vector<T>& operator=(vector<T> v)
{
  swap(v);//反正我原来的空间也要销毁,我跟你传值过来的v直接交换,而且不会改变你
  return *this;
}

2.1.7 析构函数

~vector()
{
  /*if (_start)*///delete 会自动检查空指针  没必要
  delete[] _start;
  _start = _finish = _end_of_storage = nullptr;
}

注意:delete空指针是没关系的,delete会自己判断     delete出问题一般都是野指针

2.1.8 构造函数相关的全部代码

我们发现大部分都设计要到初始化为nullptr,c11后是支持直接在成员变量那边给缺省值的,所以们可以美化一下

全部代码

//无参构造函数
    vector()
    {}
    //有参构造函数(对n个存储的类型去调用他们的构造)
    vector(size_t n, const T& val = T())
    {
      reserve(n);//因为我们知道会进多少数据,所以可以提前开空间
      for (int i = 0; i < n; ++i)
        push_back(val);
    }
    //重载一个防止间接寻址
    vector(int n, const T val = T())
    {
      reserve(n);//因为我们知道会进多少数据,所以可以提前开空间
      for (int i = 0; i < n; ++i)
        push_back(val);
    }
    //传别人的迭代器区间进行构造
    template<class InputIterator>
    vector(InputIterator first, InputIterator last)
    {
      //这里传的是别人的迭代器,不知道会传多少数据,不能提前扩容,只能让pushback的时候去判断
      while (first != last)
      {
        push_back(*first);
        ++first;
      }
    }
    //拷贝构造(传统写法)
    vector(const vector<T>& v)
    {
      _start = new T[v.capacity()];
      //memcpy(_start, v._start, sizeof(T)*v.size());  不能用memcpy 是浅拷贝
      for (size_t i = 0; i < v.size(); ++i)
        _start[i] = v._start[i];//实现重载运算符
      _finish = _start + v.size();
      _end_of_storage = _start + v.capacity();
    }
    //拷贝构造(现代写法)
    //vector(const vector<T>& v)
    //{
    //  vector<T> temp(v.begin(), v.end());//让临时对象借助迭代器区间构造出来
    //  swap(temp);//窃取革命成果
    //}
    //交换
    void swap(vector<T>& v)
    {
      std::swap(_start, v._start);
      std::swap(_finish, v._finish);
      std::swap(_end_of_storage, v._end_of_storage);
    }
    重载赋值=(传统)
    vector<T>& operator=(const vector<T>& v)
    {
      T* temp = new T[v.capacity()];
      for (int i = 0; i < v.size(); ++i)
        temp[i] = v[i];
      delete[]_start;
      _start = temp;
      _finish = _start + v.size();
      _end_of_storage = _start + v.capacity();
      return *this;
    }
    //赋值重载现代写法
    //vector<T>& operator=(vector<T> v)
    //{
    //  swap(v);//反正我原来的空间也要销毁,我跟你传值过来的v直接交换,而且不会改变你
    //  return *this;
    //}
    //析构函数
    ~vector()
    {
      /*if (_start)*///delete 会自动检查空指针  没必要检查
      delete[] _start;
      _start = _finish = _end_of_storage = nullptr;
    }

2.2 常见接口

2.2.1 获取size和capacity

//获取size
size_t size() const
{
  return _finish - _start;
}
//获取capacoty
size_t capacity() const
{
  return _end_of_storage - _start;
}

2.2.2 判空

//判空
    bool empty() const
    {
      return _start == _finish;
    }

2.2.3 重载[ ]

1.可读可写[ ]

//重载[](可读可写)
  T& operator[](size_t pos)
  {
    assert(pos < size());
    return _start[pos];
  }

2.可读不可写[]

//重载[](可读不可写)
  const T& operator[](size_t pos) const
  {
    assert(pos < size());
    return _start[pos];
  }

3.三种访问方法

下标

//下标遍历
for (int i = 0; i < v1.size(); ++i)
  cout << v1[i] << " ";
cout << endl;

迭代器

//迭代器遍历
  vector<int>::const_iterator it = v1.begin();
  while (it != v1.end())
  {
    cout << *it << " ";
    ++it;
  }
  cout << endl;

范围for

//范围for遍历
for (auto e : v1)
  cout << e << " ";
cout << endl;
v1.resize(100);
cout << v1.size() << endl;
for (auto e : v1)
  cout << e << " ";
cout << endl;

2.2.4 提前扩容

void reserve(size_t n)
{
  size_t sz = size();//防止丢失
  if (n > capacity())
  {
    T* temp = new T[n];
    if (_start)//如果为空,就不需要拷贝也不需要释放
    {
      for (size_t i = 0; i < sz; ++i)
        temp[i] = _start[i];
      delete[] _start;
    }
    _start = temp;
    _finish = _start + sz;
    _end_of_storage = _start + n;
  }
}

考虑到之前的memcpy拷贝问题,这里不能用memcpy了!!

还要注意的是要提前记录size(),否则原空间销毁了就找不到了。

2.2.5 提前扩容+初始化

有三种情况,第一种是给的n比原来的size小,第二种是n比size大但是比capacity小,第三种是n比capacity大,这个时候需要扩容

//提前扩容+初始化
  void resize(size_t n, T val = T())
  {
    //给小
    if (n < size())
      _finish = _start + n;
    //给大
    else
    {
      //容量不够就扩
      if (n > capacity())
        reserve(n);
      while (_finish != _start + n)
      {
        *_finish = val;
        ++_finish;
      }
    }
  }

2.2.6 尾插和尾删

void push_back(const T& val)
{
  if (_finish == _end_of_storage)
    reserve(capacity() == 0 ? 4 : 2 * capacity());
  *_finish = val;
  ++_finish;
}
//尾删
void pop_back()
{
  //防止没有元素可删
  assert(!empty());
  --_finish;
}

尾插要注意扩容之前要判断一下,因为如果是0的话怎么扩都是0

我们会发现这次的指定位置插入删除不像string那样用size_t pos 而是iterator pos

2.2.7 指定位置插入

这样写有什么问题吗??

看似好像没有什么问题,但是如果把pushback(5)去掉

为什么会这样呢?

原因就是扩容后空间变了,但是pos还是指向原来的空间!!

所以我们解决方案就是pos在扩容的时候要更新一下

iterator insert(iterator pos, const T& val)
{
  assert(pos >= _start);
  assert(pos <= _finish);
  if (_finish == _end_of_storage)
  {
    size_t len = pos - _start;//记录相对距离,方便更新pos
    reserve(capacity() == 0 ? 4 : 2 * capacity());
    pos = _start + len;
  }
  iterator end = _finish - 1;
  while (end >= pos)
  {
    *(end + 1) = *end;
    --end;
  }
  *pos = val;
  ++_finish;
  return pos;
}

2.2.8 指定位置删除

返回值是pos的下一个位置

iterator erase(iterator pos)
  {
    assert(pos >= _start);
    assert(pos < _finish);
    iterator start = pos + 1;
    while (start != _finish)
    {
      *(start - 1) = *start;
      ++start;
    }
    --_finish;
    return pos;
  }

2.3 迭代器失效问题

会引起其底层空间改变的操作,都有可能使得迭代器失效。

比如:resize、reserve、insert、erase、 push_back等。

2.3.1.insert的失效

就是因为扩容导致pos失效,我们需要去及时更新pos

     但是我们传的pos是值传递,所以我们更新的后pos更新,我们在后面解引用pos就会出现经典的解引用野指针问题。

那我们怎么传回pos呢??就得用返回值!!这也是为什么insert的返回值用iterator的原因,我们想继续用的话就得去接收一下返回值,就可以了

    虽然有了返回值,我们可以去接收更新后的pos,但是一旦我们使用了任意一个可能扩容的函数,都会到时pos的失效,从而有可能回引发野指针问题,这个问题是不太好避免的,所以我们认为迭代器只能用一次,因为结果不可预测!

2.3.2 erase的失效

       erase 删除 pos 位置元素后,pos 位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果 pos 刚好是最后一个元素,删完之后 pos 刚好是 end 的位置,而 end 位置是没有元素的,那么 pos 就失效了。因此删除 vector 中任意位置上元素时,vs 就认为该位置迭代器失效了。

vs和g++对比

结果是未定义的!!不同编译器场景可能不同,严格来说vs更严谨

思考:

假设没有强制检查(比如我们自己写的vector),想删除删除 vector 中所有偶数

但是如果只有4个

为什么会这样呢,我们画图分析

  从这边我们也能看到为什么erase返回值也要用iterator的原因,我们想继续用的话就得去接收一下返回值

2.3.3 扩容导致的失效

可能本来还能用,但是中间扩容过,所以也不能用了

用pos前用一样reserve,也会失效

总而言之:尽量不要复用pos迭代器,因为任何一个可能扩容的操作都会导致失效

2.4 比较不常用的接口

2.4.1 清理元素

void clear() const
  {
    _finish = _start;
  }

2.4.2 缩容

void shrink_to_fit()
{
  size_t sz = size();//记录
  T* temp = new T[sz];
  for (size_t i = 0; i < sz; ++i)
    temp[i] = _start[i];
  delete _start;
  _start = temp;
  _finish = _start + sz;
  _end_of_storage = _start + sz;
}

2.5 反向迭代器

这里博主直接上代码,等list模拟实现的时候再放在一起分析

1、利用正向迭代器去封装反向迭代器

//反向迭代器的封装
template<class iterator, class Ref>
struct ReverseIterator
{
  typedef ReverseIterator<iterator, Ref>  Self;//Ref单纯是为了控制解引用的时候是否可以被写
  //利用反向迭代器的类来封装正向迭代器,同时在类里面设置反向迭代器的行为
  ReverseIterator(iterator it)
    :_cur(it)
  {}
  Ref operator*()
  {
    iterator temp = _cur;
    --temp;
    return *temp;
  }
  Self& operator++()
  {
    --_cur;
    return *this;
  }
  Self operator++(int)
  {
    iterator temp = _cur;
    --_cur;
    return temp;
  }
  Self& operator--()
  {
    ++_cur;
    return *this;
  }
  Self operator--(int)
  {
    iterator temp = _cur;
    ++_cur;
    return temp;
  }
  Self operator+(int n)
  {
    iterator temp = _cur;
    temp-=n;
    return temp;
  }
  Self operator-(int n)
  {
    iterator temp = _cur;
    temp+= n;
    return temp;
  }
  bool operator!=(const Self& s)
  {
    return _cur != s._cur;
  }
  bool operator==(const Self& s)
  {
    return _cur == s._cur;
  }
  //成员变量
  iterator _cur;
};

2、rebegin和rend

//反向迭代器(可读可写)
reverse_iterator rbegin()
{
  return reverse_iterator(end());
}
reverse_iterator rend()
{
  return reverse_iterator(begin());
}
//反向迭代器(可读不可写)
const_reverse_iterator rbegin() const
{
  return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
  return const_reverse_iterator(begin());
}

三,vector实现的全部代码

namespace cyx
{
  //反向迭代器的封装
  template<class iterator, class Ref>
  struct ReverseIterator
  {
    typedef ReverseIterator<iterator, Ref>  Self;//Ref单纯是为了控制解引用的时候是否可以被写
    //利用反向迭代器的类来封装正向迭代器,同时在类里面设置反向迭代器的行为
    ReverseIterator(iterator it)
      :_cur(it)
    {}
    Ref operator*()
    {
      iterator temp = _cur;
      --temp;
      return *temp;
    }
    Self& operator++()
    {
      --_cur;
      return *this;
    }
    Self operator++(int)
    {
      iterator temp = _cur;
      --_cur;
      return temp;
    }
    Self& operator--()
    {
      ++_cur;
      return *this;
    }
    Self operator--(int)
    {
      iterator temp = _cur;
      ++_cur;
      return temp;
    }
    Self operator+(int n)
    {
      iterator temp = _cur;
      temp-=n;
      return temp;
    }
    Self operator-(int n)
    {
      iterator temp = _cur;
      temp+= n;
      return temp;
    }
    bool operator!=(const Self& s)
    {
      return _cur != s._cur;
    }
    bool operator==(const Self& s)
    {
      return _cur == s._cur;
    }
    //成员变量
    iterator _cur;
  };
  template<class T>
  class vector
  {
  public:
    //正向迭代器
    typedef T* iterator;
    typedef const T* const_iterator;
    //反向迭代器
    typedef ReverseIterator<iterator, T&> reverse_iterator;
    typedef ReverseIterator<iterator, const T&> const_reverse_iterator;
    //正向迭代器(可读可写)
    iterator begin()
    {
      return _start;
    }
    iterator end()
    {
      return _finish;
    }
    //正向迭代器(可读不可写)
    iterator begin() const
    {
      return _start;
    }
    iterator end() const
    {
      return _finish;
    }
    //反向迭代器(可读可写)
    reverse_iterator rbegin()
    {
      return reverse_iterator(end());
    }
    reverse_iterator rend()
    {
      return reverse_iterator(begin());
    }
    //反向迭代器(可读不可写)
    const_reverse_iterator rbegin() const
    {
      return const_reverse_iterator(end());
    }
    const_reverse_iterator rend() const
    {
      return const_reverse_iterator(begin());
    }
    //无参构造函数
    vector()
    {}
    //有参构造函数(对n个存储的类型去调用他们的构造)
    vector(size_t n, const T& val = T())
    {
      reserve(n);//因为我们知道会进多少数据,所以可以提前开空间
      for (int i = 0; i < n; ++i)
        push_back(val);
    }
    //重载一个防止间接寻址
    vector(int n, const T val = T())
    {
      reserve(n);//因为我们知道会进多少数据,所以可以提前开空间
      for (int i = 0; i < n; ++i)
        push_back(val);
    }
    //传别人的迭代器进行构造
    template<class InputIterator>
    vector(InputIterator first, InputIterator last)
    {
      //这里传的是别人的迭代器,不知道会传多少数据,不能提前扩容,只能让pushback的时候去判断
      while (first != last)
      {
        push_back(*first);
        ++first;
      }
    }
    //拷贝构造(传统写法)
    vector(const vector<T>& v)
    {
      _start = new T[v.capacity()];
      //memcpy(_start, v._start, sizeof(T)*v.size());  不能用memcpy 是浅拷贝
      for (size_t i = 0; i < v.size(); ++i)
        _start[i] = v._start[i];//实现重载运算符 完成深拷贝
      _finish = _start + v.size();
      _end_of_storage = _start + v.capacity();
    }
    //拷贝构造(现代写法)
    //vector(const vector<T>& v)
    //{
    //  vector<T> temp(v.begin(), v.end());//让临时对象借助迭代器区间构造出来
    //  swap(temp);//窃取革命成果
    //}
    //交换
    void swap(vector<T>& v)
    {
      std::swap(_start, v._start);
      std::swap(_finish, v._finish);
      std::swap(_end_of_storage, v._end_of_storage);
    }
    //重载赋值=(传统)
    vector<T>& operator=(const vector<T>& v)
    {
      assign(v.begin(), v.end());
      return *this;
    }
    //赋值重载现代写法
    //vector<T>& operator=(vector<T> v)
    //{
    //  swap(v);//反正我原来的空间也要销毁,我跟你传值过来的v直接交换,而且不会改变你
    //  return *this;
    //}
    //assign的迭代器区间替换
    template <class InputIterator>  
    void assign(InputIterator first, InputIterator last)
    {
      size_t sz = last - first;
      T* temp = new T[sz];
      for (int i = 0; i < sz; ++i)
        temp[i] =*(first+i);
      delete[]_start;
      _start = temp;
      _finish = _end_of_storage = _start + sz;
    }
    //assign的直接替换
    void assign(size_t n, const T& val)
    {
      T* temp = new T[n];
      for (int i = 0; i < n; ++i)
        temp[i] = val;
      delete[]_start;
      _start = temp;
      _finish = _end_of_storage = _start + n;
    }
    assign的直接替换 重载//防止间接寻址
    void assign(int n, const T& val)
    {
      T* temp = new T[n];
      for (int i = 0; i < n; ++i)
        temp[i] = val;
      delete[]_start;
      _start = temp;
      _finish = _end_of_storage = _start + n;
    }
    //
    //析构函数
    ~vector()
    {
      /*if (_start)*///delete 会自动检查空指针
      delete[] _start;
      _start = _finish = _end_of_storage = nullptr;
    }
    //获取size
    size_t size() const
    {
      return _finish - _start;
    }
    //获取capacoty
    size_t capacity() const
    {
      return _end_of_storage - _start;
    }
    //判空
    bool empty() const
    {
      return _start == _finish;
    }
    //重载[](可读可写)
    T& operator[](size_t pos)
    {
      assert(pos < size());
      return _start[pos];
    }
    //重载[](可读不可写)
    const T& operator[](size_t pos) const
    {
      assert(pos < size());
      return _start[pos];
    }
    //提前扩容+初始化
    void resize(size_t n, T val = T())
    {
      //给小
      if (n < size())
        _finish = _start + n;
      //给大
      else
      {
        //容量不够就扩
        if (n > capacity())
          reserve(n);
        while (_finish != _start + n)
        {
          *_finish = val;
          ++_finish;
        }
      }
    }
    //提前扩容
    void reserve(size_t n)
    {
      size_t sz = size();//防止丢失
      if (n > capacity())
      {
        T* temp = new T[n];
        if (_start)//如果为空,就不需要拷贝也不需要释放
        {
          for (size_t i = 0; i < sz; ++i)
            temp[i] = _start[i];
          delete[] _start;
        }
        _start = temp;
        _finish = _start + sz;
        _end_of_storage = _start + n;
      }
    }
    //尾插
    void push_back(const T& val)
    {
      if (_finish == _end_of_storage)
        reserve(capacity() == 0 ? 4 : 2 * capacity());
      *_finish = val;
      ++_finish;
    }
    //尾删
    void pop_back()
    {
      //防止没有元素可删
      assert(!empty());
      --_finish;
    }
    //指定位置插入
    iterator insert(iterator pos, const T& val)
    {
      assert(pos >= _start);
      assert(pos <= _finish);
      if (_finish == _end_of_storage)
      {
        size_t len = pos - _start;
        reserve(capacity() == 0 ? 4 : 2 * capacity());
        pos = _start + len;
      }
      iterator end = _finish - 1;
      while (end >= pos)
      {
        *(end + 1) = *end;
        --end;
      }
      *pos = val;
      ++_finish;
      return pos;
    }
    //指定位置删除
    iterator erase(iterator pos)
    {
      assert(pos >= _start);
      assert(pos < _finish);
      iterator start = pos + 1;
      while (start != _finish)
      {
        *(start - 1) = *start;
        ++start;
      }
      --_finish;
      return pos;
    }
    //清理元素
    void clear() const
    {
      _finish = _start;
    }
    //缩容
    void shrink_to_fit()
    {
      size_t sz = size();//记录
      T* temp = new T[sz];
      for (size_t i = 0; i < sz; ++i)
        temp[i] = _start[i];
      delete _start;
      _start = temp;
      _finish = _start + sz;
      _end_of_storage = _start + sz;
    }
  private:
    iterator _start= nullptr;
    iterator _finish= nullptr;
    iterator _end_of_storage= nullptr;
  };

有什么不懂得可以问博主哦!后面有时间再来细化接口

相关文章
|
1天前
|
存储 算法 C++
【C++】详解STL容器之一的 vector
【C++】详解STL容器之一的 vector
|
6天前
|
C++ 容器
【c++】优先级队列|反向迭代器(vector|list)
【c++】优先级队列|反向迭代器(vector|list)
5 0
|
6天前
|
C++ 容器
C++之评委打分案例(vector与deque容器练习)
C++之评委打分案例(vector与deque容器练习)
9 1
|
6天前
|
算法 C++ 容器
C++之vector容器操作(构造、赋值、扩容、插入、删除、交换、预留空间、遍历)
C++之vector容器操作(构造、赋值、扩容、插入、删除、交换、预留空间、遍历)
15 0
|
7天前
|
存储 算法 编译器
【C++航海王:追寻罗杰的编程之路】vector
【C++航海王:追寻罗杰的编程之路】vector
9 0
|
8天前
|
算法 编译器 Linux
【C++/STL】:vector容器的底层剖析&&迭代器失效&&隐藏的浅拷贝
【C++/STL】:vector容器的底层剖析&&迭代器失效&&隐藏的浅拷贝
9 0
|
8天前
|
存储 算法 C++
【C++/STL】:vector容器的基本使用
【C++/STL】:vector容器的基本使用
15 1
|
9天前
|
存储 安全 算法
C++的内置数组和STL array、STL vector
C++的内置数组和STL array、STL vector
|
11天前
|
存储 算法 C++
C++一分钟之-容器概览:vector, list, deque
【6月更文挑战第21天】STL中的`vector`是动态数组,适合随机访问,但插入删除非末尾元素较慢;`list`是双向链表,插入删除快但随机访问效率低;`deque`结合两者优点,支持快速双端操作。选择容器要考虑操作频率、内存占用和性能需求。注意预分配容量以减少`vector`的内存重分配,使用迭代器而非索引操作`list`,并利用`deque`的两端优势。理解容器内部机制和应用场景是优化C++程序的关键。
22 5
|
18天前
|
编译器 C++ 容器
【C++进阶】深入STL之vector:深入研究迭代器失效及拷贝问题
【C++进阶】深入STL之vector:深入研究迭代器失效及拷贝问题
23 0