C++——vector模拟实现(上)

简介: 笔记

vecotr模拟实现


源代码里面,核心成员是下面红框三个

1.png

观察这里的代码发现这里的迭代器都是原生指针

2.png3.png4.png

#include<iostream>
#include<assert.h>
using namespace std;
namespace my_space
{
  template<class T>
  class vector
  {
  public:
    typedef T* iterator;
    typedef const T* const_iterator;
    iterator begin()
    {
      return _start;
    }
    iterator end()
    {
      return _finish;
    }
    const_iterator begin() const
    {
      return _start;
    }
    const_iterator end() const
    {
      return _finish;
    }
    vector()
      :_start(nullptr)
      , _finish(nullptr)
      , _end_of_storage(nullptr)
    {}
    ~vector()
    {
      delete[] _start;
      _start = _finish = _end_of_storage = nullptr;
    }
    size_t capacity() const
    {
      return _end_of_storage - _start;
    }
    const T& operator[](size_t pos) const
    {
      assert(pos < size());
      return _start[pos];
    }
    T& operator[](size_t pos)
    {
      assert(pos < size());
      return _start[pos];
    }
    size_t size() const
    {
      return _finish - _start;
    }
    void reserve(size_t n)
    {
      if (n > capacity())
      {
        size_t sz = size();
        T* tmp = new T[n];
        if (_start)
        {
          memcpy(tmp, _start, sizeof(T) * sz);
          delete[] _start;
        }
        _start = tmp;
        _finish = _start + sz;
        _end_of_storage = _start + n;
      }
    }
    void push_back(const T& x)//加const防止隐式类型转换的中间常量传参错误,引用可避免深拷贝
    {
        if (_finish == _end_of_storage)
        {
          reserve(capacity() == 0 ? 4 : capacity() * 2);
        }
        *_finish = x;
        ++_finish;
    }
    void pop_back()
    {
      assert(_finish > _start);
      --_finish;
    }
  private:
    iterator _start;
    iterator _finish;
    iterator _end_of_storage;
  };
};

insert


  void insert(iterator pos, const T& x)
    {
      assert(pos >= _start);
      assert(pos <= _finish);
      if (_finish == _end_of_storage)
      {
        reserve(capacity() == 0 ? 4 : capacity() * 2);
      }
      // 挪动数据
      iterator end = _finish - 1;
      while (end >= pos)
      {
        *(end + 1) = *end;
        --end;
      }
      *pos = x;
      ++_finish;
    }

这种写法有一个缺陷

在3的前面插入30,此时程序正常运行

5.png

屏蔽掉一条语句后,会出错

6.png

这是因为扩容时出现了问题,pos本来是指向start中的,扩容之后start被销毁,新空间是tmp,pos就成了野指针

7.pngpos失效了

    void insert(iterator pos, const T& x)
    {
      assert(pos >= _start);
      assert(pos <= _finish);
      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;
    }

这样就不会出错了,用相对位置记住pos之前所在位置

8.png9.png

如果连续插入有时会报错,有时则不会

这是因为pos的修改不会影响p,pos是形参,p有可能会失效,有可能不会失效取决于有没有扩容,所以在p位置插入数据后,不要再去访问p,因为有可能会失效

但是如果用引用,就跟库里面的insert不一致了,我们尽量要跟库里面保持一致

image.png

还有一种情况,当空间足够时,我们插入数据也会崩,也会导致迭代器失效11.png12.png

it指向2时满足条件 ,在2的前面插入4

13.png

然后++it,it仍然指向2,这样每次++it会导致it一直指向2,所以程序会崩


修改方法就是用一个变量来接收insert的返回值,库里面的insert,会返回新插入数据的下标,我们接收这个下标跟库里面的insert保持一致


这样就可以


如果时偶数,对it++俩次即可  

14.png

  iterator insert(iterator& pos, const T& x)
    {
      assert(pos >= _start);
      assert(pos <= _finish);
      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;
    }
void test_vector1()
  {
    vector<int> v;
    v.reserve(10);
    v.push_back(1);
    v.push_back(2);
    v.push_back(4);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);
    auto it = v.begin();
    while (it != v.end())
    {
      if (*it % 2 == 0)
      {
        it = v.insert(it, *it * 2);
        ++it;
      }
      ++it;
    }
    for (size_t i = 0; i < v.size(); ++i)
    {
      cout << v[i] << " ";
    }
    cout << endl;
  }

15.png去掉reserve扩容之后,插入很多数据,程序仍然可以正常运行

16.png

erase


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

17.png

如果earse设置了缩容,缩容是以时间换空间,效率较低,缩容也会存在迭代器失效问题

使用上面代码删除所有的偶数

此时正常打印

18.png

但这种情况有崩溃

19.png

此时又没有删掉

20.png21.png

当it走到2时,用4去覆盖掉it ,然后++it,it指向了3

22.png

it指向4之后,5过去覆盖,之后++it,it指向finish

23.png

这时因为it错开了第一个4,++导致it快了一步

同理

24.png

it指向2,3把it覆盖了,++it,然后it指向了4

25.png

指向4之后,finish又覆盖4,++it,it成了野指针

26.png

所以会崩溃

所以:insert/erase pos位置,不要随便访问pos,一定要更新,直接访问可能会让迭代器失效

解决办法,使用时加个else语句即可


27.png

相关文章
|
8天前
|
C++
c++的学习之路:13、vector(2)
c++的学习之路:13、vector(2)
18 0
|
1月前
|
编译器 C++
C++:vector增删查改模拟实现
C++:vector增删查改模拟实现
56 0
|
1月前
|
存储 C++ 容器
【C++】vector的底层剖析以及模拟实现
【C++】vector的底层剖析以及模拟实现
|
1月前
|
编译器 C++ 容器
【C++练级之路】【Lv.7】【STL】vector类的模拟实现
【C++练级之路】【Lv.7】【STL】vector类的模拟实现
|
1月前
|
存储 算法 测试技术
C++:Vector的使用
C++:Vector的使用
|
1月前
|
存储 算法 C++
【C/C++ Vector容量调整】理解C++ Vector:Reserve与Resize的区别与应用
【C/C++ Vector容量调整】理解C++ Vector:Reserve与Resize的区别与应用
57 1
|
1月前
|
存储 C语言 C++
C++初阶--自我实现vector
C++初阶--自我实现vector
|
1月前
|
程序员 C++ 索引
在C++语言中Vector的命名空间的作用
在C++语言中Vector的命名空间的作用
15 0
|
6天前
|
存储 缓存 编译器
【C++进阶(五)】STL大法--list模拟实现以及list和vector的对比
【C++进阶(五)】STL大法--list模拟实现以及list和vector的对比
|
6天前
|
编译器 C++
【C++进阶(三)】STL大法--vector迭代器失效&深浅拷贝问题剖析
【C++进阶(三)】STL大法--vector迭代器失效&深浅拷贝问题剖析