C++之打造my vector篇(下)

简介: C++之打造my vector篇(下)

C++之打造my vector篇(上)https://developer.aliyun.com/article/1624988

3.扩展延伸,深度理解代码

在VS环境下,比较严格,在迭代器方面比较严格,特别是失效迭代器的访问。

迭代器失效问题

在测试接口的过程中,有个bug就是迭代器失效问题

我们知道迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*

因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即 如果继续使用已经失效的迭代器,程序可能会崩溃)。

1.对vector进行扩容操作,像resize,reserve等操作

还有就是在insert,push_back操作过程中涉及了扩容

#include <iostream>
using namespace std;
#include <vector>
int main()
{
    vector<int> v{ 1,2,3,4,5,6 };
    auto it = v.begin();
    // 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
   // v.resize(100, 8);
    // reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容
    //量改变
         v.reserve(100);
         // 插入元素期间,可能会引起扩容,而导致原空间被释放
        v.insert(v.begin(), 0);
          v.push_back(8);
         // 给vector重新赋值,可能会引起底层容量改变
        //v.assign(100, 8);
    /*
   出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释
   放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块
   已经被释放的空间,而引起代码运行时崩溃。
   解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给
   it重新赋值即可。
   */
    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    return 0;
}

修改后的代码

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v{ 1,2,3,4,5,6 };
    // 在修改vector之后,重新获取迭代器
    auto it = v.begin();
    
    v.reserve(100);
    v.insert(v.begin(), 0);
    v.push_back(8);
    // 如果使用v.assign(100, 8);,也需要在之后重新获取迭代器
    // 重新获取迭代器,因为之前的操作可能会改变vector的内存
    it = v.begin();
    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    return 0;
}

还有一点就是insert数据过后,即使没有扩容,指向容器中插入点之后的所有迭代器、指针和引用都可能失效。

所以当我们继续访问修改p的位置数据,已经失效了,需要更新失效的迭代器。

由于数据挪动,p的指向改变了,所以我们认为迭代器也失效了。

v.insert(p, 40);

p=v.insert(p, 40);
(*(p+1)) *= 10;

2.erase的删除导致的迭代器失效问题

void test_vector3()
  {
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    print_container(v);
    // 删除所有的偶数
    auto it = v.begin();
    while (it != v.end())
    {
      if (*it % 2 == 0)
      {
        v.erase(it);
      }
      else
      {
        ++it;
      }
    }
    print_container(v);
  }
}

当我们用VS std中的接口时,会发现直接报错

所以我们也要进行重新的更新

it=v.erase(it);

#include <iostream>
using namespace std;
#include <vector>
    int main()
    {
        int a[] = { 1, 2, 3, 4 };
        vector<int> v(a, a + sizeof(a) / sizeof(int));
        // 使用find查找3所在位置的iterator
        vector<int>::iterator pos = find(v.begin(), v.end(), 3);
        // 删除pos位置的数据,导致pos迭代器失效。
        v.erase(pos);
        cout << *pos << endl; // 此处会导致非法访问
        return 0;
    }

使用memcpy的拷贝问题

当我们想拷贝几个字符串时,就会出现问题了。

问题分析:

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

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

void reserve(size_t n) {
  if (n > capacity()) {
    size_t oldsize = size();
    T* temp = new T[n];
       memcpy(tmp, _start, old_size * sizeof(T));
/*
    for (size_t i = 0; i < oldsize; i++) {
      temp[i] = _start[i];
    }
*/
    delete[]_start;
    _start = temp;
    _finish =temp + oldsize;
    _end_of_storage = temp+n;
  }
}

当我们使用这个memcpy版本进行扩容插入时,程序会出现问题

测试代码

void vector5() {
  vector<string> v;
  v.push_back("11111111111111111111");
  v.push_back("11111111111111111111");
  v.push_back("11111111111111111111");
  v.push_back("11111111111111111111");
  print_container(v);
  v.push_back("11111111111111111111");
  print_container(v);
}

memcpy是浅拷贝,temp和原来的v指向了同一块空间,当调用了delete[]时,11111111...字符串被析构了,空间释放,变成随机值,后面又delete,free _start ,这时候temp指向的是释放的空间。

所以我们可以调用赋值,就可以解决问题,本质调用string的赋值,其他类型赋值一样的。

旧空间释放就不会影响新空间。

for (size_t i = 0; i < oldsize; i++) {
           temp[i] = _start[i];

4.完整代码复现

#pragma once
#include <iostream>
#include <assert.h>
#include <vector>
#include <string>
using namespace std;
namespace my_vector {
  template<class T>
  class vector {
  public:
    typedef T* iterator;
    typedef const T* const_iterator;
    vector()
    {}
    vector(const vector<T>& v) {
      reserve(v.size());
      for (auto e : v) {
        push_back(e);
      }
    }
    /*
    vector<T>operator=(const vector<T>& v) {
      if (this != v) {
        reserve(v.size());
        for (auto e : v) {
          push_back(e);
        }
      }
      return this;
    }
    */
    template <class InputIterator>
    vector(InputIterator first, InputIterator last) {
      while (first != last) {
        push_back(*first);
        first++;
      }
    }
    void swap(vector<T>& v)
    {
      std::swap(_start, v._start);
      std::swap(_finish, v._finish);
      std::swap(_end_of_storage, v._end_of_storage);
    }
    // v1 = v3
    //vector& operator=(vector v)
    vector<T>& operator=(vector<T> v)
    {
      swap(v);
      return *this;
    }
    void clear() {
      _finish = _start;
    }
    ~vector() {
      if (_start) {
        delete[]_start;
        _start = _finish = _end_of_storage = nullptr;
      }
    }
    iterator begin() {
      return _start;
    }
    iterator end() {
      return _finish;
    }
    const_iterator begin()const {
      return _start;
    }
    const_iterator end() const {
      return _finish;
    }
    void reserve(size_t n) {
      if (n > capacity()) {
        size_t oldsize = size();
        T* temp = new T[n];
        memcpy(temp, _start, oldsize*sizeof(T));
        /*
        for (size_t i = 0; i < oldsize; i++) {
          temp[i] = _start[i];
        }
        */
        delete[]_start;
        _start = temp;
        _finish = temp + oldsize;
        _end_of_storage = temp + n;
      }
    }
    void resize(size_t n, T val = T()) {
      if (n < size()) {
        _finish = _start + n;
      }
      else {
        reserve(n);
        while (_finish < _start + n) {
          *_finish = val;
          _finish++;
        }
      }
    }
    void push_back(const T& x) {
      if (_finish == _end_of_storage) {
        reserve(capacity() == 0 ? 4 : capacity() * 2);
      }
      *_finish = x;
      ++_finish;
    }
    void pop_back() {
      assert(!empty());
      --_finish;
    }
    void erase(iterator pos) {
      assert(pos >= _start);
      assert(pos <= _finish);
      iterator it = pos + 1;
      while (it != end()) {
        *(it - 1) = *it;
        it++;
      }
      _finish--;
    }
    iterator insert(iterator pos, const T& x) {
      assert(pos >= _start && 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 = *(end - 1);
        end--;
      }
      *pos = x;
      _finish++;
      return pos;
    }
    size_t size()const {
      return _finish - _start;
    }
    size_t capacity()const {
      return _end_of_storage - _start;
    }
    bool empty()const {
      return _start == _finish;
    }
    T& operator[](size_t i) {
      assert(i < size());
      return _start[i];
    }
    const T& operator[](size_t i) const
    {
      assert(i < size());
      return _start[i];
    }
  private:
    iterator _start = nullptr;
    iterator _finish = nullptr;
    iterator _end_of_storage = nullptr;
  };
  template<class T>
  void print_vector(const vector<T>& v) {
    //typename vector<T>::const_iterator it = v.begin();
    auto it = v.begin();
    while (it != v.end()) {
      cout << *it << " ";
      it++;
    }
    cout << '\n';
    for (auto e : v) {
      cout << e << " ";
    }
    cout << endl;
  }
  template<class Container>
  void print_container(const Container& v) {
    /*
    auto it = v.begin();
    while (it != v.end()) {
      cout << *it << " ";
      it++;
    }
    cout << '\n';
    */
    for (auto e : v) {
      cout << e << " ";
    }
    cout << endl;
  }
  void vector1() {
    vector<int>v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);
    v.push_back(6);
    int x;
    cin >> x;
    auto p = find(v.begin(), v.end(), x);
    if (p != v.end()) {
      p = v.insert(p, 40);
      (*(p + 1)) *= 10;
    }
    //v.pop_back();
    //v.pop_back();
    //v.insert(v.begin() + 2, 5);
    //v.erase(v.begin() + 3);
    for (size_t i = 0; i < v.size(); i++) {
      cout << v[i] << endl;
    }
  }
  void vector2() {
    vector<int>v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);
    v.push_back(6);
    print_vector(v);
    vector<int>v1(v);
    vector <int>v2 = v;
    vector<int> v3(v1.begin(), v1.begin() + 3);
    print_container(v1);
    print_container(v2);
    print_container(v3);
    /*
    vector<double>v1;
    v1.push_back(1.1);
    v1.push_back(2.2);
    v1.push_back(3.3);
    v1.push_back(4.4);
    v1.push_back(5.5);
    v1.push_back(6.6);
    print_container(v1);
    */
  }
  void vector3() {
    vector<int> v;
    v.resize(10, 1);
    v.reserve(20);
    print_container(v);
    cout << v.size() << endl;
    cout << v.capacity() << endl;
    v.resize(15, 2);
    print_container(v);
    v.resize(25, 3);
    print_container(v);
    v.resize(5);
    print_container(v);
  }
  void vector4() {
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(4);
    v.push_back(5);
    print_container(v);
    // 删除所有的偶数
    auto it = v.begin();
    while (it != v.end())
    {
      if (*it % 2 == 0)
      {
        v.erase(it);
      }
      else {//不加else,不会删除连续的偶数,会++两次
        ++it;
      }
    }
    print_container(v);
  }
  void test_vector3()
  {
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    print_container(v);
    // 删除所有的偶数
    auto it = v.begin();
    while (it != v.end())
    {
      if (*it % 2 == 0)
      {
        it=v.erase(it);
      }
      else
      {
        ++it;
      }
    }
    print_container(v);
  }
  void vector5() {
    vector<string> v;
    v.push_back("11111111111111111111");
    v.push_back("11111111111111111111");
    v.push_back("11111111111111111111");
    v.push_back("11111111111111111111");
    print_container(v);
    v.push_back("11111111111111111111");
    print_container(v);
  }
}

结束语

本期博客就到此结束啦,相信通过自己对vector的实现,大家对vector有了更深的了解。

最后希望友友们给小编点点赞吧,感谢各位友友的支持!!!

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