vector的模拟实现

简介: vector的模拟实现

上一篇我们对vector一些常用的函数进行了讲解,本篇博客我们就对vector进行模拟实现,以便于我们更好地了解vector的使用以及对一些常见bug的认识

有了string类的模拟实现,vector的模拟实现我们上手起来就简单一点了:

首先为了和库里面的vector混淆视听,放入自己命名的空间里,并且根据vector的源码分析我们得出了三个成员变量:

分别是:

其实他们实质上都是指针,位置大概是这样的,遵循左闭右开的规则

_start
_finish
_endofstorage

这样一个简单的框架就构造出来了:

template是模板初阶我们学习过的,里面的T可以是char,int等任意类型

namespace jh 
{
  template <class T>
  class vector
  {
  public:
    typedef T* iterator;
    typedef const T* const_iterator;
  private:
    iterator _start;
    iterator _finish;
    iterator _endofstorage;
  };
};
capacity和size

capacity就是endofstorage到start之间的可存储元素个数

size就是finish到start之间的有效元素个数

size_t capacity() const
{
  return _endofstorage - _start;
}
size_t size() const
{
  return _finish - _start;
}
pushback尾插函数

尾插函数在很多地方可以复用,所以我们首先解决了尾插,为后面的函数进行模拟实现提供了基础:

插入首先就是要判断是否已满,所以我们先检查是否需要扩容,然后将当前finish位置的值置为x,finish的位置要往后移动一位

void push_back(const T& x)
{
  if (_finish==_endofstorage)
  {
    reserve(capacity() == 0 ? 4 : capacity() * 2);
  }
  *(_finish) = x;
  _finish++;
}
insert插入函数

同样的insert插入函数也需要复用,我们来解决它:

我们首先做的就是断言,pos一定是要在start和finish的区间之内的

当finish和endofstorage相等时就需要扩容,但是这里考虑到迭代器失效的问题我们就要记录pos和start原本之间的元素个数,然后再给start赋值

最后用while循环移动元素,移动完成后将pos位置的值置为x,同时finish位置往后移动一位即可

void insert(iterator pos, const T& x)
{
  assert(pos <= _finish);
  assert(pos >= _start);
  if (_finish == _endofstorage)
  {
    size_t len = pos - _start;
    reserve(capacity() == 0 ? 4 : capacity() * 2);
    _start = pos + len;//考虑到扩容之后迭代器失效的问题,所以记录位置
  }
  iterator end = _finish - 1;
  while (end >= pos)
  {
    *(end + 1) = *(end);
    end--;
  }
  *pos = x;
  _finish++;
}
迭代器

迭代器是很常用的一个知识点,相信我们之前的学习中早已经熟悉了他的用法,很简单,但是我们需要对权限的放大有一个照顾,所以加上const

iterator begin()
{
  return _start;
}
iterator end()
{
  return _finish;
}
const_iterator begin()const
{
  return _start;
}
const_iterator end()const
{
  return _finish;
}
构造函数

构造函数主要用于初始化,他的作用很大也很常见,但是这里vector的构造函数可以用一个特殊的迭代器初始化

template <class InputIterator>
vector(InputIterator first, InputIterator last)
  :_start(nullptr)
  ,_finish(nullptr)
  ,_endofstorage(nullptr)
{
  while (first != last)
  {
    push_back(*first);
    first++;
  }
}

还可以以这种方式,T()默认就是0,和上面一样,直接用pushback尾插进去,当然这里的T()其实是C++的一个匿名函数,通常我们所说匿名对象的生命周期只有一行,但是用const修饰后的匿名对象的生命周期会延长!

vector(size_t n, const T& val = T())
{
  reserve(n);
  for (size_t i = 0; i < n; i++)
  {
    push_back(val);
  }
}
拷贝构造函数

拷贝构造我们直接用for循环解决,然后调用pushback ,很简单的

vector(const vector<T>& v)
{
  reserve(v.capacity());
  for (auto e : v)
  {
    push_back(e);
  }
}
析构函数

析构函数直接重拳出击了属于是(狗头)

用delete清空_start的空间,然后将其全部置为空

~vector()
{
  delete[] _start;
  _start = _finish = _endofstorage = nullptr;
}
swap函数

swap函数其实我们的用处不大,我们直接复用std标准库里的swap:

void swap(vector<T>& v)
{
  std::swap(_start, v._start);
  std::swap(_finish, v._finish);
  std::swap(_endofstorage, v._endofstorage);
}
赋值操作符函数重载

也很简单,我们偷点懒用直接复用swap函数:

交换后返回*this即可

vector<T>& operator=(vector<T> tmp)
{
  swap(tmp);
  return *this;
}
下标访问符重载

直接返回start下标指定下标的值即可,当然前提是这里的pos是要小于size的

T& operator[](size_t pos)
    {
      assert(pos < size());
      return _start[pos];
    }
    const T& operator[](size_t pos) const
    {
      assert(pos < size());
      return _start[pos];
    }
resize函数和reserve函数

其实我们可以将reserve先实现后直接将reserve复用再resize上,直接扩容

reserve扩容时只有n是大于原本的capacity时才会扩容,向前几篇博客所说的,不会进行缩容,然后我们记录有效元素个数sz=size(提前记录好是因为后面会进行delete释放原本start空间的操作),如果start不为空,就将start中的元素按照深拷贝的方式用for循环放入提前开辟好的tmp空间里,然后将tmp给予start,finish依旧时start+size,但是endofstorage变成了start+n

void reserve(size_t n)
{
  if (n > capacity())
  {
    T* tmp = new T[n];
    size_t sz = size();
    if (_start)
    {
      for (size_t i = 0; i < sz; i++)
      {
        tmp[i] = _start[i];
      }
      delete[] _start;
    }
    _start = tmp;
    _finish = _start + sz;
    _endofstorage = _start + n;
  }
}

resize函数的扩容我们就用reserve来实现,但是resize可以会初始化:

void resize(size_t n, const T& val = T())
{
  if (n <= size())
  {
    _finish = _start + n;
  }
  else
  {
    reserve(n);
    while (_finish < _start+n)
    {
      *finish = val;
      finish++;
    }
  }
}
erase函数

这里有一个需要注意的点:

erase会返回被删除元素的下一个元素的迭代器!

断言我相信大家都是信手拈来了

我们将pos位置后的元素往前移动一位即可!

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

好了,今天的分享到这里就结束了,感谢大家的支持!

相关文章
|
8天前
|
C++ 容器
vector模拟实现
vector模拟实现
5 0
|
2月前
|
存储 编译器 C语言
C++:Vector的模拟实现
C++:Vector的模拟实现
|
2月前
|
存储 C++ 容器
vector的模拟实现
vector的模拟实现
55 0
|
9月前
|
算法 C++
vector使用和模拟实现
vector使用和模拟实现
|
11月前
|
C++
【vector的模拟实现】
【vector的模拟实现】
27 0
|
12月前
|
存储 算法 编译器
【C++】vector的使用及其模拟实现
vector的使用、底层原理及其模拟实现
|
编译器 C++
C++之模拟实现vector(上)
C++之模拟实现vector(上)
67 0
|
C++ 容器
C++之模拟实现vector(下)
C++之模拟实现vector(下)
76 0
|
存储 编译器 Linux
vector的模拟实现
我们之前说过范围for的底层是迭代器,我们实现了迭代器,也可以使用范围for遍历容器,因为在编译器编译时会自动将范围for替换为迭代器的形式,记住这是傻瓜式的替换,意思是你的迭代器不能修改,比如我们把begin变成Begin,这时候范围for就编译不过去了。