[C++ ,STL]vector模拟实现

vector才算是打开了泛型编程的大门,在之前的几篇博客中你可以明显看到vector的构造,等接口的参数多了一个templete,毕竟他可以存任何的数据类型,着篇博客会涉及到一些STL源码(让我们看看大神是如何实现的),且依旧沿袭上篇博客,我只实现我觉得比较难的接口


文章目录

  • 1. 基本框架
    • 0 help 函数
    • 1 迭代器
    • 2 构造函数
    • 3 modify
    • 4 capacity

1. 基本框架

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4V1VAljf-1639720382918)(/Users/wuxiaobo/Library/Application Support/typora-user-images/image-20211213161351874.png)]

源码中是用iterator 创建了三个变量,start(开始)finish(结尾) end_of_storage(容量(capacity))

经过上篇博客的洗礼iterator应该比较熟悉了吧,其实就是迭代器,但是vector底层是一个线性的结构也就是数组,那么Iterator一个T*


0 help 函数

开空间,深拷贝,现代写法

  void Construction(size_t N=1)//开辟空间
         {
             N=N>capacity()?N:capacity()*2;
             Vector<T>tmp(N);
             for(int i =0;i<capacity();i++)
             {
                 tmp[i]=start[i];
               (tmp.finish)++;
             }
          swap(tmp, *this);
         }

构造列表函数,每个构造函数都要写独立封装一个让代码更简洁

void StructList(const size_t num)
{
    start=new T[num+1];
    finish=start;
    end_of_storage=start+num+1;
}

1 迭代器

上面基础结构有用的了迭代器那么我们优先事项这一块

        typedef int* iterator;
         iterator begin()
         {
            return start;
         }
         iterator end()
         {
             return finish;
         }
         const iterator begin()const
         {
             return start;
         }
         const iterator end()const
         {
             return finish;
         }
         
         iterator rebegin()
         {
             return finish-1;//最后一个元素的前一个元素
         }
         iterator reend()
         {
             return start-1;//第一个元素的上一个元素的
         }



2 构造函数


默认构造

Vector(size_t num =5)
{
    StructList(num);//构造列表函数
}

拷贝构造

    vector(const vector<T>& v)//用另一个vector初始化
        {
          StructList(num);//构造列表函数
            _start=new T[v.size()];
            _finish=_endOfStorage=_start+v.size();

            auto tmp=v._start;
            for(int i=0;i<v.size();i++)
            {
                _start[i]=tmp[i];
            }
        }

赋值拷贝

void operator =(Vector<T>val)
 {
    swap(*this,val);
 }

迭代器区间构造

也就是和stirng 不同的地方,且后面的容器基本都支持用迭代器区间初始化,泛型编程必备,现阶段看这个实现和拷贝,用一个模版去推你是啥类型的迭代器

   template <class InputIterator>
         Vector(InputIterator first,InputIterator end)
         {
            Construction(end-first);
           while(first!=end)
           {
               start=*first++;
           }
           finish=end-first;
         }



3 modify

push_bakc

不难发现的与string对比起来基础没差,一样没有空间开空间,插入数据…………,但是这里数据类型是根据模版显示实例化的 ,本质就是底层是物理的连续空间

void push_back(const T&val)//T看到了吗,来咯泛型编程的第一个接口
         {
             if(finish>=end_of_storage)//开辟空间
             {
                 Construction();
             }
             *finish=val;
              finish++;
         }

[]

沿袭string的传统,[]需要重载俩个一个为const版的,一个为非const版本的,这一块操作赋重载又又发挥了作用

代码:

   T& operator[](const size_t index)
        {
             return start[index];
        }
         const T& operator[](const size_t index)const 
         {
             return start[index];
         }


insert

这一块涉及了一个知识叫做迭代器失效,请观看这篇 iterator 博客,这里就不深入研究了,这儿有俩构造

  1. 常数作为下标
  2. 迭代器作为下标(实现方法一直上面的说了vector的迭代器就是一个指针)
 const size_t&insert ( const  size_t & pos,const T&val)
         {
             if(size()+1>=capacity())
             {
                 Construction();
             };
             for(int i=size();i>pos;i--)
             {
                 start[i]=start[i-1];
             }
             start[pos]=val;
             finish++;
             return pos++;//返回下一个元素的下标
         }

erase

   const size_t&erast (const  size_t&  pos)
         {
             finish--;
             int ret=pos;
             int count=size()-ret-1;//需要挪动的次数
             while(count--)
             {
                 start[ret]=start[++ret];
             }
            return pos+1;//pos下一个位置的下标
         }

因为底层是一个数组,所以像这类insert与erase这种接口最好少用,复杂度太高(数据如果有几亿个数据然后头叉)


4 capacity

size

这一块稍微与string有些出入的是,他没有用size和capacity去记录,而是用指针去指向了末尾元算,但是幸运的是,他物理空间是连续的,且指针是可以减指针的(不理解请参考博主这篇博客),那么finish-start那不就得到元素的个数吗

代码:

size_t size()
         {
             return finish-start;
         }

reserve

沿袭string,容器中的reserve基本都是大于capacity就开空间小于啥也不干

void reserve(size_t n)
{
     if(n>capacity())//开辟空间
    {
        Construction(n);
    }
}

resize

这个接口需要考虑三个条件

  1. 大于capacity,增容,并初始化
  2. 小于size,缩短finish为n的长度,不更改end_of_storage
  3. 在size<=N<=capacity,,初始化数据到N的位置

代码:

void resize(size_t n,const T&numb)//源码中会构造一个缺省值,这我们就直接不搞了
         {

             if(n<size())
             {
                 finish=start+n;
             }
             else if(n>capacity())//开辟空间
             {
                 Construction(n);
             }

             for(int i=size();i<n;i++)//初始化空间
             {
                 *finish++=numb;
             }
         }

上面仔细看会发现vector中的参数一般都是模版参数,而string就是char,因为string就是为字符而生的,vector为大众服务,后面的容器和vector一样都是好同志为大众服务

你可能感兴趣的:(c++,c++,开发语言,后端)