【C++初阶】七、STL---vector模拟实现

简介: 目录一、模拟实现接口总览1.1 接口总览1.2 vector整体框架1.3 vector成员变量介绍二、vector模拟实现2.1 构造函数2.1.1 无参构造2.1.2 迭代器区间构造2.1.3 n个val构造2.1.4 拷贝构造2.2 赋值运算符重载2.2.1 传统写法2.2.2 现代写法2.3 析构函数2.4 Iterator2.4.1 begin2.4.2 end2.5 Capacity2.5.1 size2.5.2 capacity2.5.3 empty2.5.4 resize2.5.5 re

目录

一、模拟实现接口总览

1.1 接口总览

1.2 vector整体框架

1.3 vector成员变量介绍

二、vector模拟实现

2.1 构造函数

2.1.1 无参构造

2.1.2 迭代器区间构造

2.1.3 n个val构造

2.1.4 拷贝构造

2.2 赋值运算符重载

2.2.1 传统写法

2.2.2 现代写法

2.3 析构函数

2.4 Iterator

2.4.1 begin

2.4.2 end

2.5 Capacity

2.5.1 size

2.5.2 capacity

2.5.3 empty

2.5.4 resize

2.5.5 reserve

2.6 element access

2.6.1 operator[]

2.7 Modifiers

2.7.1 swap

2.7.2 push_back

2.7.3 pop_back

2.7.4 insert

2.7.5 erase

三、vector模拟实现全部代码


一、模拟实现接口总览

1.1 接口总览

Memberfunctions//构造函数vector()
//拷贝构造 -- 现代写法2vector(constvector<T>&v)
//迭代器区间构造template<classInputIterator>vector(InputIteratorfirst, InputIteratorlast)
//n个val构造vector(size_tn, constT&val=T())
//n个val构造 -- 重载,解决第一个参数的int size_t 的问题vector(intn, constT&val=T())
//析构~vector()
//赋值重载 -- 现代写法2vector<T>&operator=(vector<T>v)
//iteratoriteratorbegin()
iteratorend()
const_iteratorbegin()constconst_iteratorend()const//capacitysize_tsize()constsize_tcapacity()constboolempty()constvoidreserve(size_tn)
voidresize(size_tn, Tval=T())
//element accessT&operator[](size_tpos)
constT&operator[](size_tpos)const//modifiervoidswap(vector<T>&v)
voidpush_back(constT&x)
voidpop_back()
//任意位置插入 -- 插入后认为迭代器失效,  迭代器失效 : 扩容引起,野指针问题iteratorinsert(iteratorpos, constT&val)
//任意位置删除 -- erase 之后也认为 pos 迭代器失效iteratorerase(iteratorpos)

       上面也是挑一些常用的进行模拟实现,要把自己实现的写在自己命名的命名空间里面,否则与库中的 vector 会产生冲突

注:

       vector类模拟实现,最主要也是实现 vector 的构造、拷贝构造、赋值运算符重载以及析构函数

1.2 vector整体框架

#pragma once#include<iostream>#include<assert.h>usingnamespacestd;
namespacefy{
template<classT>classvector    {
public:
//vector的迭代器是一个原生指针typedefT*iterator;
typedefconstT*const_iterator;
private:
iterator_start;// 指向数据块的开始iterator_finish;// 指向数据块的结尾iterator_endOfStorage;//指向存储容量的尾    };
}


注:

       后面的 STL 的模拟实现我们参考的是SGI版 STL3.0 的写法,写法跟上一个 string 模拟实现有所不同,虽然表面上看起来不一样,但是实际上表达的效果是大同小异的

1.3 vector成员变量介绍

在vector当中有三个成员变量_start、_finish、_endofstorage

  1. _start指向容器的头
  2. _finish指向容器当中有效数据的尾
  3. _endofstorage指向整个容器的尾

image.png

  • 指针减指针 就可以得到大小或容量
  • _finish - _start = size(有效数据的大小)
  • _endOfStorage - _start = capacity(整个容器的容量)

image.png

二、vector模拟实现

2.1 构造函数

2.1.1 无参构造

这里要完成的工作是初始化,直接将构造对象的三个成员变量都设置为 nullptr

//构造函数vector()
    :_start(nullptr)
    , _finish(nullptr)
    , _endOfStorage(nullptr)
{}

2.1.2 迭代器区间构造

       vector 支持使用一段迭代器区间进行对象的构造,但是这里要注意:该迭代器区间可以是其他容器的迭代器区间,也就是说该函数接收到的迭代器的类型是不确定的,所以我们这里需要将该构造函数设计为一个函数模板,然后再将数据一个个尾插即可

//迭代器区间构造template<classInputIterator>//模板函数vector(InputIteratorfirst, InputIteratorlast)
    :_start(nullptr)
    , _finish(nullptr)
    , _endOfStorage(nullptr)
{
while (first!=last)//尾插    {
push_back(*first);
++first;
    }
}

2.1.3 n个val构造

       vector 也支持 n 个 val 构造,比如构造10个1。对于该函数,可以先利用 reserve 进行扩容,一次性开好所需要的空间,然后进行尾插即可

//n个val构造vector(size_tn, constT&val=T())
    :_start(nullptr)
    , _finish(nullptr)
    , _endOfStorage(nullptr)
{
reserve(n);//扩容for (size_ti=0;i<n; ++i)//把数据进行尾插    {
push_back(val);
    }
}

2.1.3 n个val构造

       vector 也支持 n 个 val 构造,比如构造10个1。对于该函数,可以先利用 reserve 进行扩容,一次性开好所需要的空间,然后进行尾插即可

//n个val构造vector(size_tn, constT&val=T())
    :_start(nullptr)
    , _finish(nullptr)
    , _endOfStorage(nullptr)
{
reserve(n);//扩容for (size_ti=0;i<n; ++i)//把数据进行尾插    {
push_back(val);
    }
}

但是,这里存在一个问题,执行这句代码时直接报错

fy::vector<int> v5(10, 2);

image.png

       这是因为 这句代码调用的不是 n个 val构造,而是调用了迭代器区间构造,迭代器区间构造对参数 first 和 last 进行了解引用,而 int类型不能进行解引用操作,所以报错说非法寻址

       造成这个的直接原因是 n 个 val构造的第一个参数 size_t ,与 fy::vector v5(10, 2) 第一个参数类型不匹配,编译器就会优先调用迭代器区间构造,要解决这个问题可以对 v5(10, 2) 第一个参数强制类型转化成 size_t,但是这样会给使用者不舒服,另一种就是函数重载

//n个val构造 -- 重载,解决第一个参数的int size_t 的问题vector(intn, constT&val=T())
    :_start(nullptr)
    , _finish(nullptr)
    , _endOfStorage(nullptr)
{
reserve(n);
for (inti=0; i<n; ++i)
    {
push_back(val);
    }
}

2.1.4 拷贝构造

拷贝构造也分传统写法和现代写法,推荐现代写法,注意深浅拷贝的问题,这里明显就是要进行深拷贝

(1)传统写法

思想:先开辟一块与该容器大小相同的空间,然后将该容器当中的数据拷贝过来

拷贝构造//传统写法vector(constvector<T>&v)
{
T*tmp=newT[v.capacity()];//开辟一块大小相同的空间memcpy(tmp, v._start, sizeof(T) *v.capacity());//拷贝_start=tmp;//更新_start_finish=_start+v.size();//更新_finish_endOfStorage=_start+v.capacity();//更新_endOfStorage}

       但是这里存在一个严重的问题,当 vector 存储的数据是内置类型或无需进行深拷贝的自定义类型时,使用 memcpy 函数是没什么问题的,但当vector存储的数据是需要进行深拷贝的自定义类型时,使用 memcpy 函数的弊端就体现出来

比如:vector 存储的数据是 string 时

image.png

vector 里面的每个储存对象都是 string,并且 string 也指向自己所开辟的空间,即指向自己储存的字符串

image.png

       使用 memcpy 函数进行拷贝构造的话,那么拷贝构造出来的 vecto r当中存储的每个 string 的成员变量的值,将与被拷贝的vector当中存储的每个 string 的成员变量的值相同,即两个vector当中的每个对应的string成员都指向同一个字符串空间,进行析构的时候就会出问题,string 指向的空间会被释放两次

image.png

如何解决?一个个进行深拷贝就可以了

vector(constvector<T>&v)
{
_start=newT[v.capacity()];//开辟一块大小相同的空间for (size_ti=0; i<v.size(); i++) //将容器v当中的数据一个个拷贝过来    {
_start[i] =v[i];//利用了赋值重载            }
_finish=_start+v.size();//更新_finish_endOfStorage=_start+v.capacity();//更新_endOfStorage}

        这里利用了 string类的赋值重载,string类的赋值运算符重载函数就是深拷贝

拷贝完了之后结果是:

image.png

这样就完成了深拷贝

       所以,C语言的关于内存接口直接使用大多数是有问题的,尽量少使用 C语言的关于内存方面接口,C语言的关于内存方面接口对内置类型无影响,对自定义类型的影响就很大了

(2)现代写法

复用迭代器区间构造,再交换一下数据即可

//拷贝构造 -- 现代写法vector(constvector<T>&v)
    :_start(nullptr)
    , _finish(nullptr)
    , _endOfStorage(nullptr)
{
vector<T>tmp(v.begin(), v.end());
swap(tmp);
}

注:swap 后面实现

2.2 赋值运算符重载

       vector 的赋值运算符重载也涉及深拷贝问题,也有传统写法和现代写法

2.2.1 传统写法

       判断是否是给自己赋值,若是给自己赋值则无需进行操作;若不是给自己赋值,则先开辟一块和 v 大 小相同的空间,然后将容器 v 当中的数据拷贝

赋值重载//传统写法vector<T>&operator=(constvector<T>&v)
        {
if (this==&v)
            {
return*this;//检查自我赋值            }
delete[] _start;
T*tmp=newT[v.capacity()];
memcpy(tmp, v._start, sizeof(T) *v.capacity());//拷贝_start=tmp;
_finish=_start+v.size();
_endOfStorage=_start+v.capacity();
return*this;
        }

这里也是 memcpy 这个问题,跟上面一样,也是利用 赋值重载,一个个进行深拷贝就可以了

vector<T>& operator=(const vector<T>& v)
{
  if (this == &v)
  {
    return *this;//检查自我赋值
  }
  delete[] _start;
  _start = new T[v.capacity()];//开空间,与v一致
  for (size_t i = 0; i < v.size(); i++) //将容器v当中的数据一个个拷贝过来
  {
    _start[i] = v[i];
  }
  _finish = _start + v.size();
  _endOfStorage = _start + v.capacity();
  return *this;
}

2.2.2 现代写法

(1)现代写法1

复用拷贝构造,构造出 tmp,然后交换一下就可以了

//赋值重载 -- 现代写法1vector<T>&operator=(constvector<T>&v)
{
if (this==&v)
    {
return*this;//检查自我赋值    }
vector<T>tmp(v);//复用拷贝构造swap(tmp);//复用swapreturn*this;
}

(2)现代写法2

        也是复用拷贝构造,但是参数不再是传引用传参,而是传值形参,而传值传参则会自动调用拷贝构造函数,但是这种写法无法检查自我赋值的情况,但是这种情况几乎不会出现,推荐这种写法

//赋值重载 -- 现代写法2vector<T>&operator=(vector<T>v)//传值传参自动调用拷贝构造,不能检查自我赋值的问题,但不影响程序正确性{
swap(v);
return*this;
}

2.3 析构函数

释放空间,置空即可

//析构~vector()
{
delete[] _start;
_start=_finish=_endOfStorage=nullptr;
}

2.4 Iterator

       vector 的迭代器实际上也是指针,范围 for 底层也是迭代器

//vector的迭代器是一个原生指针typedefT*iterator;
typedefconstT*const_iterator;

2.4.1 begin

返回容器的首地址即可

//iteratoriteratorbegin()
{
return_start;
}

2.4.1 begin

返回容器的首地址即可

//iteratoriteratorbegin()
{
return_start;
}

const 版本的,只能对数据进行读操作,而不能进行修改

const_iteratorbegin()const{
return_start;
}

2.4.2 end

返回容器当中有效数据的下一个数据的地址

iteratorend()
{
return_finish;
}

2.4.2 end

返回容器当中有效数据的下一个数据的地址

iteratorend()
{
return_finish;
}

const 版本的,只能对数据进行读操作,而不能进行修改

const_iteratorend()const{
return_finish;
}

2.5 Capacity

2.5.1 size

返回数据个数,指针相减即可

size_tsize()const{
return_finish-_start;
}

2.5.2 capacity

返回容器实际容量,指针相减即可

size_tcapacity()const{
return_endOfStorage-_start;
}

2.5.2 capacity

返回容器实际容量,指针相减即可

size_tcapacity()const{
return_endOfStorage-_start;
}

2.5.3 empty

       直接通过比较容器当中的_start和_finish指针的指向来判断容器是否为空,若所指位置相同,则该容器为空

boolempty()const{
return_start==_finish;
}boolempty()const{
return_start==_finish;
}

2.5.4 resize

resize 规则:

  1. 当所给值 n大于容器当前的 size时,将size扩大到 n,扩大的元素为第二个所给值 val,若未给出,则默认为容器所存储类型的默认构造函数所构造出来的值
  1. 当所给值 n小于容器当前的 size时,将size缩小到 n
voidresize(size_tn, Tval=T())
voidresize(size_tn, Tval=T())
{
if (n>capacity())//检查是否需要扩容    {
reserve(n);
    }
if (n>size())
    {
while (_finish<_start+n)
        {
*_finish=val;
++_finish;
        }
    }
else    {
_finish=_start+n;//n < size,有效数据缩减    }
}       

       第二个参数,默认给的是其对应类型的缺省值作为 "填充值"。由于这里我们不知道具体类型是什么,这里缺省值我们使用匿名对象 T(),使用匿名对象后 val 为容器所存储类型的默认构造函数所构造出来的值

2.5.5 reserve

reserve 规则:

  1. n大于对象当前的 capacity时,将capacity扩大到n或大于n
  2. n小于对象当前的 capacity时,什么也不做,也不缩容
voidreserve(size_tn)
{
if (n>capacity())//n大于 capacity 才扩容,reserve不缩容    {
size_toldSize=size();
T*tmp=newT[n];
if (_start)
        {
//memcpy对自定义类型会有浅拷贝问题,需要对每个元素使用拷贝构造进行深拷贝//memcpy(tmp, _start, sizeof(T) * oldSize);//errordelete[] _start;
        }
_start=tmp;
_finish=tmp+oldSize;
_endOfStorage=_start+n;
    }
}

这里也是 memcpy 这个问题,跟上面一样,也是利用 赋值重载,一个个进行深拷贝就可以了

voidreserve(size_tn)
{
if (n>capacity())//n大于 capacity 才扩容,reserve不缩容    {
size_toldSize=size();//记录原来的size,避免扩容不能确定 _finishT*tmp=newT[n];
if (_start)
        {
for (size_ti=0; i<oldSize; ++i)
            {
tmp[i] =_start[i];//利用赋值重载            }
delete[] _start;
        }
_start=tmp;
_finish=tmp+oldSize;
_endOfStorage=_start+n;
    }
}

2.6 element access

2.6.1 operator[]

vector也支持我们使用 “下标+[ ]” 的方式对容器当中的数据进行访问,实现时直接返回对应位置的数据即可

T&operator[](size_tpos)
{
assert(pos<size());
return_start[pos];
}

const 版本,只读,不能对数据进行修改

constT&operator[](size_tpos)const{
assert(pos<size());
return_start[pos];
}

2.7 Modifiers

2.7.1 swap

       swap 函数用于交换两个容器的数据,直接调用库当中的 swap 函数将两个容器当中的各个成员数据进行交换即可

voidswap(vector<T>&v)
{
std::swap(_start, v._start);//使用库里面的 swap 函数std::swap(_finish, v._finish);
std::swap(_endOfStorage, v._endOfStorage);
}

2.7.2 push_back

注意判断数据是否满,直接在尾上插入数据即可

voidpush_back(constT&x)
{
if (_finish==_endOfStorage)
    {
size_tnewCapacity=capacity() ==0?4 : capacity() *2;
reserve(newCapacity);
    }
*_finish=x;
++_finish;
}

2.7.3 pop_back

尾删数据,注意判空

1. void pop_back()
2. {
3.  assert(!empty());
4.  --_finish;
5. }

2.7.4 insert

       这里要注意迭代器失效的问题,insert函数可以在所给迭代器pos位置插入数据,分四步:① 检查 pos 是否越界   ② 检查是否需要扩容  ③ 移动数据   ④ 插入数据

//任意位置插入 -- 插入后认为迭代器失效,  迭代器失效 : 扩容引起,野指针问题iteratorinsert(iteratorpos, constT&val)
{
assert(pos>=_start&&pos<_finish);
//扩容会导致迭代器失效/*if (size() == capacity()){size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);}*/if (size() ==capacity())
    {
size_tlen=pos-_start;
size_tnewCapacity=capacity() ==0?4 : capacity() *2;
reserve(newCapacity);
// 扩容会导致pos迭代器失效,需要更新处理一下pos=_start+len;
    }
iteratorend=_finish-1;
while (end>=pos)//挪动数据    {
*(end+1) =*(end);
--end;
    }
*pos=val;
++_finish;
returnpos;
}

2.7.5 erase

这里也要注意迭代器失效的问题,erase函数可以删除所给迭代器pos位置的数据

//任意位置删除 -- erase 之后也认为 pos 迭代器失效iteratorerase(iteratorpos)
{
assert(pos>=_start&&pos<_finish);
iteratorbegin=pos+1;
while (begin<_finish)//挪动数据    {
*(begin-1) =*(begin);
++begin;
    }
--_finish;
returnpos;
}

注:此处没有新开辟空间,也就没有野指针问题,erase 缩容也可能导致 pos 迭代器失效(具体看编译器实现),SGI版不缩容

       看一下文档对 erase 返回值的描述,返回被删除位置的后一个位置的迭代器,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了,所以统一认为 erase 之后 pos 迭代器失效

       迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃) 对于 vector对于 vector可能会导致其迭代器失效的操作有

  1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等
  2. 指定位置元素的删除操作:erase

vector 的模拟实现两大问题:深拷贝和迭代器失效

       而 string 模拟实现的时候并不注意迭代器失效这个问题,因为 string 大多数用的都是下标进行各项操作,基本不用迭代器进行操作,所以就忽略了

三、vector模拟实现全部代码

vector.h

#pragma once#include<iostream>#include<assert.h>usingnamespacestd;
namespacefy{
template<classT>classvector    {
public:
//vector的迭代器是一个原生指针typedefT*iterator;
typedefconstT*const_iterator;
//构造函数vector()
            :_start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {}
//拷贝构造传统写法//vector(const vector<T>& v)//{//  _start = new T[v.capacity()];//开辟一块大小相同的空间//  for (size_t i = 0; i < v.size(); i++) //将容器v当中的数据一个个拷贝过来//  {//      _start[i] = v[i];//  }//  _finish = _start + v.size();//更新_finish//  _endOfStorage = _start + v.capacity();//更新_endOfStorage//}//vector(const vector<T>& v)//{//  T* tmp = new T[v.capacity()];//开辟一块大小相同的空间//  memcpy(tmp, v._start, sizeof(T) * v.capacity());//拷贝,error//  _start = tmp;//更新_start//  _finish = _start + v.size();//更新_finish//  _endOfStorage = _start + v.capacity();//更新_endOfStorage//}//拷贝构造 -- 传统写法2/*vector(const vector<t>& v):_start(nullptr), _finish(nullptr), _endOfStorage(nullptr){reserve(v.capacity());for (const auto& e : v){push_back(e);}}*///拷贝构造 -- 现代写法vector(constvector<T>&v)
            :_start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
vector<T>tmp(v.begin(), v.end());
swap(tmp);
        }
//迭代器区间构造template<classInputIterator>//模板函数vector(InputIteratorfirst, InputIteratorlast)
            :_start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
while (first!=last)//尾插            {
push_back(*first);
++first;
            }
        }
//n个val构造vector(size_tn, constT&val=T())
            :_start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
reserve(n);//扩容for (size_ti=0;i<n; ++i)//把数据进行尾插            {
push_back(val);
            }
        }
//n个val构造 -- 重载,解决第一个参数的int size_t 的问题vector(intn, constT&val=T())
            :_start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
reserve(n);
for (inti=0; i<n; ++i)
            {
push_back(val);
            }
        }
//析构~vector()
        {
delete[] _start;
_start=_finish=_endOfStorage=nullptr;
        }
//赋值重载传统写法//vector<T>& operator=(const vector<T>& v)//{//  if (this == &v)//  {//      return *this;//检查自我赋值//  }//  delete[] _start;//  _start = new T[v.capacity()];//开空间,与v一致//  for (size_t i = 0; i < v.size(); i++) //将容器v当中的数据一个个拷贝过来//  {//      _start[i] = v[i];//  }//  //  _finish = _start + v.size();//  _endOfStorage = _start + v.capacity();//  return *this;//}//vector<T>& operator=(const vector<T>& v)//{//  if (this == &v)//  {//      return *this;//检查自我赋值//  }//  delete[] _start;//  T* tmp = new T[v.capacity()];//  memcpy(tmp, v._start, sizeof(T) * v.capacity());//  _start = tmp;//  _finish = _start + v.size();//  _endOfStorage = _start + v.capacity();//  return *this;//}赋值重载--现代写法1//vector<T>& operator=(const vector<T>& v)//{//  if (this == &v)//  {//      return *this;//检查自我赋值//  }//  vector<T> tmp(v);//复用拷贝构造//  swap(tmp);//复用swap//  return *this;//}//赋值重载 -- 现代写法2vector<T>&operator=(vector<T>v)//传值传参自动调用拷贝构造,不能检查自我赋值的问题,但不影响程序正确性        {
swap(v);
return*this;
        }
//-------------------------------------------------------------//iteratoriteratorbegin()
        {
return_start;
        }
iteratorend()
        {
return_finish;
        }
const_iteratorbegin()const        {
return_start;
        }
const_iteratorend()const        {
return_finish;
        }
//-------------------------------------------------------------//capacitysize_tsize()const        {
return_finish-_start;
        }
size_tcapacity()const        {
return_endOfStorage-_start;
        }
boolempty()const        {
return_start==_finish;
        }
voidreserve(size_tn)
        {
if (n>capacity())//n大于 capacity 才扩容,reserve不缩容            {
size_toldSize=size();//记录原来的size,避免扩容不能确定 _finishT*tmp=newT[n];
if (_start)
                {
for (size_ti=0; i<oldSize; ++i)
                    {
tmp[i] =_start[i];//                    }
delete[] _start;
                }
_start=tmp;
_finish=tmp+oldSize;
_endOfStorage=_start+n;
            }
//if (n > capacity())//n大于 capacity 才扩容,reserve不缩容//{//  size_t oldSize = size();//  T* tmp = new T[n];//  if (_start)//  {//      //memcpy对自定义类型会有浅拷贝问题,需要对每个元素使用拷贝构造进行深拷贝//      //memcpy(tmp, _start, sizeof(T) * oldSize);//error//      delete[] _start;//  }//  _start = tmp;//  _finish = tmp + oldSize;//  _endOfStorage = _start + n;//}        }
voidresize(size_tn, Tval=T())
        {
if (n>capacity())//检查是否需要扩容            {
reserve(n);
            }
if (n>size())
            {
while (_finish<_start+n)
                {
*_finish=val;
++_finish;
                }
            }
else            {
_finish=_start+n;//n < size,有效数据缩减            }
        }       
//-------------------------------------------------------------//element accessT&operator[](size_tpos)
        {
assert(pos<size());
return_start[pos];
        }
constT&operator[](size_tpos)const        {
assert(pos<size());
return_start[pos];
        }
//-------------------------------------------------------------//Modifiersvoidswap(vector<T>&v)
        {
std::swap(_start, v._start);//使用库里面的 swap 函数std::swap(_finish, v._finish);
std::swap(_endOfStorage, v._endOfStorage);
        }
voidpush_back(constT&x)
        {
if (_finish==_endOfStorage)
            {
size_tnewCapacity=capacity() ==0?4 : capacity() *2;
reserve(newCapacity);
            }
*_finish=x;
++_finish;
        }
voidpop_back()
        {
assert(!empty());
--_finish;
        }
//任意位置插入 -- 插入后认为迭代器失效,  迭代器失效 : 扩容引起,野指针问题iteratorinsert(iteratorpos, constT&val)
        {
assert(pos>=_start&&pos<_finish);
//扩容会导致迭代器失效/*if (size() == capacity()){size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newCapacity);}*/if (size() ==capacity())
            {
size_tlen=pos-_start;
size_tnewCapacity=capacity() ==0?4 : capacity() *2;
reserve(newCapacity);
// 扩容会导致pos迭代器失效,需要更新处理一下pos=_start+len;
            }
iteratorend=_finish-1;
while (end>=pos)
            {
*(end+1) =*(end);
--end;
            }
*pos=val;
++_finish;
returnpos;
        }
//任意位置删除 -- erase之后也认为 pos 迭代器失效iteratorerase(iteratorpos)
        {
assert(pos>=_start&&pos<_finish);
iteratorbegin=pos+1;
while (begin<_finish)//挪动数据            {
*(begin-1) =*(begin);
++begin;
            }
--_finish;
returnpos;
        }
private:
iterator_start;// 指向数据块的开始iterator_finish;// 指向数据块的结尾iterator_endOfStorage;//指向存储容量的尾    };
}

Test.cpp

#include "vector.h"voidvectorTest()
{
//构造fy::vector<int>v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
for (size_ti=0; i<v1.size(); ++i)
    {
cout<<v1[i] <<" ";
    }
cout<<endl;
fy::vector<int>::iteratorit=v1.begin();
while (it!=v1.end())
    {
cout<<*it<<" ";
++it;
    }
cout<<endl;
for (autoe : v1)
    {
cout<<e<<" ";
    }
cout<<endl;
//拷贝构造cout<<"--拷贝--"<<endl;
fy::vector<int>v2(v1);
for (autoe : v2)
    {
cout<<e<<" ";
    }
cout<<endl;
//赋值重载cout<<"--赋值重载--"<<endl;
fy::vector<int>v3;
v3=v1;
for (autoe : v2)
    {
cout<<e<<" ";
    }
cout<<endl;
//迭代器区间构造cout<<"--迭代器区间构造--"<<endl;
fy::vector<int>v4(v1.begin(), v1.end());
for (autoe : v4)
    {
cout<<e<<" ";
    }
cout<<endl;
v4.pop_back();
v4.pop_back();
v4.pop_back();
v4.pop_back();
v4.pop_back();
for (autoe : v4)
    {
cout<<e<<" ";
    }
cout<<endl;
//n个val构造cout<<"--n个val构造--"<<endl;
fy::vector<int>v5(10, 2);
for (autoe : v5)
    {
cout<<e<<" ";
    }
cout<<endl;
cout<<"----------"<<endl;
fy::vector<char>v6(10, 'a');
for (autoe : v6)
    {
cout<<e<<" ";
    }
cout<<endl;
v6.resize(15);
for (autoe : v6)
    {
cout<<e<<" ";
    }
cout<<endl;
v6.resize(5);
for (autoe : v6)
    {
cout<<e<<" ";
    }
cout<<endl;
v6.insert(v6.begin() +3, 'x');
v6.insert(v6.begin() +3, 'z');
v6.insert(v6.begin() +3, 'x');
for (autoe : v6)
    {
cout<<e<<" ";
    }
cout<<endl;
v6.erase(v6.end() -1);
v6.erase(v6.end() -1);
v6.erase(v6.end() -1);
v6.erase(v6.end() -1);
for (autoe : v6)
    {
cout<<e<<" ";
    }
cout<<endl;
}
intmain()
{
vectorTest();
return0;
}

----------------我是分割线---------------

文章到这里就结束了,下一篇即将更新

相关文章
|
6月前
|
存储 编译器 C++
【C++ 初阶路】--- 类和对象(下)
【C++ 初阶路】--- 类和对象(下)
25 1
|
6月前
|
存储 编译器 C语言
【C++初阶路】--- 类和对象(中)
【C++初阶路】--- 类和对象(中)
28 1
|
6月前
|
安全 编译器 程序员
【C++初阶】--- C++入门(上)
【C++初阶】--- C++入门(上)
36 1
|
6月前
|
编译器 C++
【C++初阶】—— 类和对象 (下)
【C++初阶】—— 类和对象 (下)
25 2
|
6月前
|
C语言 C++
【C++初阶】—— C++内存管理
【C++初阶】—— C++内存管理
31 1
|
6月前
|
存储 编译器 C语言
【C++ 初阶路】--- 类与对象(上)
【C++ 初阶路】--- 类与对象(上)
33 0
|
6月前
|
存储 安全 编译器
【C++初阶】--- C++入门(下)
【C++初阶】--- C++入门(下)
36 0
|
6月前
|
存储 编译器 Linux
【C++初阶】--- C++入门(中)
【C++初阶】--- C++入门(中)
25 0
|
6月前
|
编译器 C++
C++练级之路——模板初阶
C++练级之路——模板初阶
18 0
|
6月前
|
JavaScript 前端开发 编译器
【C++初阶】C++模板编程入门:探索泛型编程的奥秘
【C++初阶】C++模板编程入门:探索泛型编程的奥秘
38 0