实用指南:C++篇 Vector模拟实现(1) 初始化 迭代器遍历 插入尾插尾删 一文详解

胖咕噜的稞达鸭个人主页

个人专栏: 《数据结构《C++初阶高阶》

⛺️技术的杠杆,撬动整个世界!

在这里插入图片描述
vector的使用
在学习vector之前必须了解vector文档中的相关描述;vector的介绍,这里我们挑重点的接口来进行阐述。
//注意:通篇都要看注释!精华总结感悟!!!

1.vector 的定义

构造函数声明接口说明
vector()无参构造
vector(size_type n, const value_type& val =value_type())构造并初始化n个val
vector (const vector& x);拷贝构造
vector (InputIterator first, InputIterator last);使用迭代器进行初始化构造

2. vector iterator 的使用

iterator的使用接口说明
[begin +end]获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator
rbegin +rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator

3.vector 空间增长问题

容量空间接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize改变vector的size
reserve改变vector的capacity

4.vector 增删查改

vector增删查改接口说明
push_back尾插
pop_back尾删
find查找。(注意这个是算法模块实现,不是vector的成员接口)
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
operator[]像数组一样访问

vector初始化

vector<int>v1;//无参数的构造
  vector<int>v2(10, 1);//带参数的构造,初始化1个数组,
    vector<int>v3(++v2.begin(), --v2.end());//还可以用一段迭代器区间去初始化
      //v3用v2去初始化,不想要第一个,也不想用最后一个,可以向上面那么写。v3有8个1

vector遍历

//遍历v3,下标第一种遍历方法
for (size_t i = 0; i < v3.size(); i++)
{
cout << v3[i] << " ";
}
cout << endl;
//用迭代器去遍历,要指定类域,实例化的模板参数,第二种遍历
vector<int>::iterator it = v3.begin();
  while (it != v3.end())
  {
  cout << *it << " ";
  it++;
  }
  cout << endl;
  //第三种遍历,范围for
  for (auto e : v3)
  {
  cout << e << " ";
  }
  cout << endl;

vector的扩容

void test_vector2()
{
vector<int>v(10, 1);//初始化10个1
  v.reserve(20);//扩容到20
  cout << v.capacity() << endl;//20
  cout << v.size() << endl;//10
  v.reserve(15);
  cout << v.capacity() << endl;//20
  cout << v.size() << endl;//10
  v.reserve(5);
  cout << v.capacity() << endl;//20
  cout << v.size() << endl;//10    //坚决不缩容,capacity在增加到20之后,就没有变化了
  }
  void test_vector3()
  {
  vector<int>v(10.1);
    v.reserve(20);//扩容到capacity=20,size是10,capacity是20
    cout << v.capacity() << endl;//20
    cout << v.size() << endl;//10
    v.resize(15.2);//10个1,补了5个2(调试过程中),capacity=20
    cout << v.size() << endl;
    cout << v.capacity() << endl;
    v.resize(25.3);//capacity是30,按照2倍扩容,10个1,size=25
    cout << v.size() << endl;
    cout << v.capacity() << endl;
    v.resize(5);//capacity=30,size=5,只保留5个数据,5个1
    cout << v.size() << endl;
    cout << v.capacity() << endl;
    }

vector遍历和流提取

void test_vector4()
{
vector<int>v(10, 1);
  v.push_back(2);
  v.insert(v.begin(), 0);
  //遍历要用迭代器
  for (auto e : v)
  {
  cout << e << " ";// 0  1  1  1  1  1   1   1   1   1   1   2
  }
  cout << endl;
  v.insert(v.begin() + 3, 10);//在第3个位置加一个10
  for (auto e : v)
  {
  cout << e << " ";// 0  1  1  10  1   1  1   1   1   1   1   1   2
  }
  cout << endl;
  v.clear();//实现vector的流插入流提取,方法1
  int x;
  cin >> x;
  v.push_back(x);
  vector<int> v1(5, 0);//给5个数据初始化为0,方法2:用范围for进行遍历
    for (size_t i = 0; i < 5; i++)
    {
    cin >> v1[i];//cin输入v1[i];
    }
    for (auto e : v1)
    {
    cout << e << " ";//用范围for进行遍历,输入的数字用空格分割,最后打印出55个数字
    }
    cout << endl;
    }

这里我们就有疑问了,string s2可不可以用vectorv2进行替代?
不可以! 1.string s2最后有\0,但是vectorv2没有\0
2.string可以支持比较大小,要按照ASCII码表进行比较,但是vector没有

vector的二维数组构建

void test_vector5()
{
vector<string>v1;//vector中还可以存储自定义类型
  string s1("xxxxxxx");
  v1.push_back(s1);
  v1.push_back("yyyyyy");//const char* 还可以隐式类型转换
  for (const auto& e : v1)//*it取每个值,都是一个string,string给e,进行每次拷贝构造
  //有多少个string对象就要拷贝构造多少次,所以加& ,不改变遍历的值前面加const
  //以前都是拷贝一下Int,代价比较小,如果要拷贝string,不仅要开空间还要调用数据
  {
  cout << e << " ";
  }
  cout << endl;
  //假如要实现10*5的二维数组
  //vector<vector<int>>vv();//vector里面存vector,就是二维数组
    vector<int>v(5, 1);
      vector<vector<int>>vv(10, v);//10个v,v是v(5,1)
        //vv[2][1] = 2;//访问第二行第一个并且将其修改为2    ///看图!!!
        //编译器实例化了两个vector,
        vv.operator[](2).operator[](1) = 2;//等价于vv[2][1]=2;
        for (size_t i = 0; i < vv.size(); i++)
        {
        for (size_t j = 0; j < vv[i].size(); j++)
        {
        cout << vv[i][j] << " ";
        }
        cout << endl;
        }
        cout << endl;
        }

在这里插入图片描述

这里我们用C++做一道算法题来熟悉一下vector二维数组动态规划在算法题中的应用:杨辉三角OJ
在这里插入图片描述

clase Solution{
public:
vector<vector<int>>generate(int numsRow)
  {
  vector<vector<int>>vv(numsRow);//开n行
    for (size_t i = 0; i < numsRow; ++i)
    {
    vv[i].resize(i + 1, 0);//第0行(索引)有1个,第1行(索引)有2个,每个数据默认给0
    //vv[i][0]=vv[i][vv[i].size()-1]=1;
    vv.front = vv.back = 1;
    }
    for (int i = 2; i < vv.size(); ++i)//第0行和第1行不需要
    {
    for (int j = 1; j < vv[i].size()-1; ++j)//避开每一行的第一个数据和组后一个数据
    {
    vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];//上一行的当前位置+上一行的前一个位置
    }
    }
    return vv;
    }
    }

迭代器遍历,operator[]访问

#pragma once
#include<iostream>
  #include<assert.h>
    using namespace std;
    namespace Keda
    {
    template<class T>
      class vector
      {
      public:
      typedef T* iterator;//使用typedef为T*(指向模板类型T的指针)定义了一个
      //别名iterator,用于遍历容器中的元素。表示vector类的迭代器是一个指向T类型的指针
      typedef const T* const_iterator;//const不可修改型
      iterator begin()
      {
      return _start;
      }
      iterator end()
      {
      return _finish;
      }
      const_iterator begin()const
      {
      return _start;
      }
      const_iterator end()const
      {
      return _finish;
      }
      size_t size()const
      {
      return _finish - _start;
      }
      size_t capacity()const
      {
      return _end_of_storage - _start;
      }
      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;//用于判断是否需要扩容,意为空间用完了
      };
      }

vector模板

//模板
template<class T>
  void print_vector(const vector<T>& v)//const对象就要用const迭代器
    {
    //在没有实例化的类模板里面取东西,规定:没有实例化的类模板里面取东西
    //编译器不能区分这里的const_iterator是类型还是静态成员变量
    //typename vector<T>::const_iterator it = v.begin();//也可以直接由编译器自己推导
    //静态成员变量不需要有类型
    auto it = v.begin();
    while (it != v.end())
    {
    cout << *it << " ";
    ++it;
    }
    cout << endl;
    for (auto e : v)
    {
    cout << e << " ";
    }
    cout << endl;
    }

vector插入!!!(内设及迭代器失效问题!!必看)

void reserve(size_t n)
{
if (n > capacity())
{
size_t old_size = size();
T* tmp = new T[n];//开新的空间
memcpy(tmp, _start, size() * sizeof(T));//拷贝旧空间的数据给新的空间
delete[]_start;//释放掉旧的空间
_start = tmp;//指向新的空间
_finish = tmp + old_size;
_end_of_storage = tmp + n;
}
}
void push_back(const T& x)
{
if (_finish != _end_of_storage)
{
*_finish = x;
++_finish;
}
else
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
*_finish = x;
++_finish;
}
iterator insert(iterator pos, const T& x)
{
if (_finish == _end_of_storage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : 2 * capacity());
pos = len + _start;
}
iterator end = _finish - 1;
while (end >= pos)//可以等于0
{
*(end + 1) = *(end);
--end;
}
*pos = x;
++_finish;
return pos;
}

这里会出现迭代器失效的问题:
分析:当需要插入一个新的x,很有可能出现_finish==_end_of_storage的情况,需要扩容开辟一个新的tmp指针指向一块新的空间,然后释放掉旧的_start,让tmp所在的这块空间称为新_start,然后构建好新的_finish,_end_of_storage
为什么:但是!!!!!pos还指向的是旧_start,扩容的时候如果没有考虑到pos,释放旧的_start的时候会将pos一并释放,所以再插入这里使用的pos是一个野指针
解决办法就是找到新的_start中对应旧_start的pos位置
所以在扩容的地方算出相对len,size_t len=pos-_start;扩容好了之后更新pos即可: pos=len+_start;
在这里插入图片描述
续:测试插入

void test_vector2()
{
vector<int>v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  //v.push_back(5);
  print_vector(v);
  v.insert(v.begin() + 3, 45);
  print_vector(v);
  //迭代器区间要求传左闭右开
  int x;
  cin >> x;
  auto pos = find(v.begin(), v.end(), x);
  if (pos != v.end())
  {
  //v.insert(pos, 40);
  //查找x,找到之后让pos*=10,假如pos=2,我们乘等10,应该输出20
  //(*pos) *= 10;//实际上输出400,为什么?这里也是迭代器失效的问题,所以insert以后pos就失效了,不要访问
  //pos还指向的是旧空间,再解引用就是越界的野指针,即使在insert里面做了改变,已经更新过了,但是毕竟是在内部,实参的改变不影响形参
  //那要是一定要哦访问呢???
  //这里可以将insert操作完之后在内部操作的值pos返回,iterator insert (iterator pos,const T& s); 下面return pos
  //在测试中:pos=v.insert(pos,40); (*(p+1)) *=10
  pos = v.insert(pos, 40);
  (*(pos+1) ) *= 10;
  }
  print_vector(v);
  }

这里也是迭代器失效的问题,insert以后pos就失效了,不要访问;
pos还指向的是旧空间,再解引用就是越界的野指针,即使在insert里面做了改变,已经更新过了,但是毕竟是在内部,实参的改变不影响形参(内部的更新不影响外部)

vector尾删

bool empty()
{
return _start == _finish;
}
void pop_back()
{
assert(!empty());
--_finish;
}
posted @ 2025-10-24 13:23  ycfenxi  阅读(1)  评论(0)    收藏  举报