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

相关文章
|
4天前
|
算法 编译器 C++
模拟实现c++中的vector模版
模拟实现c++中的vector模版
|
2月前
|
存储 对象存储 C++
C++ 中 std::array<int, array_size> 与 std::vector<int> 的深入对比
本文深入对比了 C++ 标准库中的 `std::array` 和 `std::vector`,从内存管理、性能、功能特性、使用场景等方面详细分析了两者的差异。`std::array` 适合固定大小的数据和高性能需求,而 `std::vector` 则提供了动态调整大小的灵活性,适用于数据量不确定或需要频繁操作的场景。选择合适的容器可以提高代码的效率和可靠性。
101 0
|
2月前
|
存储 编译器 C语言
【c++丨STL】vector模拟实现
本文深入探讨了 `vector` 的底层实现原理,并尝试模拟实现其结构及常用接口。首先介绍了 `vector` 的底层是动态顺序表,使用三个迭代器(指针)来维护数组,分别为 `start`、`finish` 和 `end_of_storage`。接着详细讲解了如何实现 `vector` 的各种构造函数、析构函数、容量接口、迭代器接口、插入和删除操作等。最后提供了完整的模拟实现代码,帮助读者更好地理解和掌握 `vector` 的实现细节。
67 0
|
3月前
|
存储 编译器 C语言
【c++丨STL】vector的使用
本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。
137 4
|
4月前
|
存储 C++ 索引
【C++打怪之路Lv9】-- vector
【C++打怪之路Lv9】-- vector
40 1
|
4月前
|
算法 C++ 容器
C++之打造my vector篇(下)
C++之打造my vector篇(下)
46 0
|
4月前
|
存储 编译器 C++
C++之打造my vector篇(上)
C++之打造my vector篇(上)
50 0
|
4月前
|
编译器 C++
【C++】—— vector模拟实现
【C++】—— vector模拟实现
|
4月前
|
算法 C++ 容器
【C++】—— vector使用
【C++】—— vector使用
|
4月前
|
存储 缓存 C++
C++番外篇——list与vector的比较
C++番外篇——list与vector的比较
37 0