【C++】vector类的模拟实现

这次选择直接展示相关接口实现,但是在此之前,需要先了解一下vector的底层实现。

一、底层实现

vector底层存的是三个迭代器(本质是指针T*,T是模版参数,代表类型)

1._start(指向第一个元素)

2._finish(指向最后一个元素的下一个元素)

3._end_of_storage(指向最大容量的下一个元素)

下面展示vector的大体框架

template <class T>
class Vector{
public:
    typedef T* iterator;//普通迭代器
    typedef const T* const_iterator;//const迭代器
public:
    //迭代器 -- 指向第一个元素
    iterator begin();
    //迭代器 -- 指向最后一个元素的下一个
    iterator end();
    //const迭代器 -- 指向第一个元素
    const_iterator begin() const;
    //const迭代器 -- 指向最后一个元素的下一个
    const_iterator end() const;
    
    //构造函数
    Vector();
    //析构函数
    ~Vector();
    //拷贝构造 v2(v1)
    Vector(const Vector<T>& v);
    //交换函数(用于深拷贝的现代写法)
    void swap(Vector<T>& v);
    //operator=
    Vector<T>& operator=(const Vector<T>& v);

    //数据个数
    size_t size() const;
    //当前容量
    size_t capacity() const;
    //将容量扩大到n
    void reserve(size_t n);
    //修改size
    void resize(size_t n, const T& x = T());

    //尾插单个元素
    void push_back(const T& x);
    //尾删单个元素
    void pop_back();
    //pos位置插入
    void insert(iterator pos, const T& x);
    //pos位置删除
    iterator easer(iterator pos);
    
    //普通对象的operator[]
    T& operator[](size_t i);
    //const对象的operator[]
    const T& operator[](size_t i) const; 
private:
    iterator _start;//指向第一个元素
    iterator _finish;//指向最后一个元素的下一个
    iterator _end_of_storage;//指向最大容量的下一个
};

二、相关接口

(一)迭代器

1.普通迭代器

适用于普通对象

//迭代器 -- 指向第一个元素
iterator begin()
{
    return _start;
}

//迭代器 -- 指向最后一个元素的下一个
iterator end()
{
    return _finish;
}

2.const迭代器

适用于const对象

//const迭代器 -- 指向第一个元素
const_iterator begin() const
{
    return _start;
}

//const迭代器 -- 指向最后一个元素的下一个
const_iterator end() const
{
    return _finish;
}

(二)默认成员函数

1.构造函数

//构造函数
Vector()
    :_start(nullptr)
    ,_finish(nullptr)
    ,_end_of_storage(nullptr)
{}

2.析构函数

~Vector()
{
    delete[] _start;
    _start = _finish = _end_of_storage = nullptr;
}

3.拷贝构造

//拷贝构造 this(v)
//把this改造的和v一模一样
Vector(const Vector<T>& v)
    //将this初始化
    :_start(nullptr)
    ,_finish(nullptr)
    ,_end_of_storage(nullptr)
{
    reserve(v.capacity());//直接开辟出所需空间大小,减少扩容带来的消耗
    for(const auto& e:v)
        push_back(e);//将v中每个元素依次尾插到this上
}

4.operator=

void swap(Vector<T>& v)
{
    ::swap(_start,v._start);//加::是为了调用库里的swap函数
    ::swap(_finish,v._finish);
    ::swap(_end_of_storage, v._end_of_storage);
}
Vector<T>& operator=(const Vector<T>& v)
{
    Vector<T> tmp(v);
    swap(tmp);//v和this交换
    return *this;
}

(三)capacity

1.数据个数  size_t size() const

//数据个数
size_t size() const
{
    return _finish - _start;//指针减指针
}

2.当前容量  size_t capacity() const

//当前容量
size_t capacity() const
{
    return _end_of_storage - _start;//指针减指针
}

3.将容量扩大到n  void reserve(size_t n)

注意⚠️:将原数组的数据拷贝到新数组时,要进行深拷贝

如果使用memcpy进行拷贝 -> 只是按字节拷贝(浅拷贝),会产生问题

正确的做法:使用operator= 进行深拷贝 -> 这样就可以实现vector<string>这类对象

//将容量扩大到n
void reserve(size_t n)
{
    //只能扩大,不能缩小
    if(n > capacity())
    {
        size_t oldSize = size();
        T* tmp = new T[n];
        //将原来的数据拷贝到新数组
        //注意原数组有数据才拷贝过去
        if(_start != nullptr)
        {
            //拷贝(⚠️一定要深拷贝,使用operator=就是深拷贝)
            for(size_t i=0; i<oldSize; i++)
            {
                tmp[i] = _start[i];//深拷贝
            }
            delete[] _start;//不为空才释放
        }
        //修改三个成员变量
        _start = tmp;
        _finish = tmp + oldSize;
        _end_of_storage = tmp + n;
    }
}

4.将当前size修改成n  void resize(size_t n, const T& x = T())

注意⚠️:T()就相当于int中的0,char中的'\0'

void resize(size_t n, const T& x = T())
{
    //缩小,直接改
    if(n < size())
    {
        _finish = _start + n;
    }
    //扩大
    else
    {
        //先扩容
        if(n > capacity())
            reserve(n);
        //在补x
        for(size_t i=size(); i<n; i++)
        {
            _start[i] = x;
        }
        //改结束标志
        _finish = _start + n;
    }
}

(四)插入删除

1.尾插单个元素  void push_back(const T& x)

//尾插单个元素
void push_back(const T& x)
{
    //先扩容
    if(_finish == _end_of_storage)
    {
        size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newcapacity);
    }
    //在尾插
    *_finish = x;
    _finish++;
}

2.尾删单个元素  void pop_back()

void pop_back()
{
    assert(_start < _finish);//防止一个都没有,还删除,就不对了
    _finish--;
}

3.pos位置插入  void insert(iterator pos, const T& x)

注意⚠️:因为传过来的pos是指向原数组的,但是如果要扩容,原数组被释放,pos就变成了野

指针。所以我们需要将pos指向新数组的对应位置。

void insert(iterator pos, const T& x)
{
    assert(pos <= _finish);//加=是为了支持尾插
    //扩容
    if(_finish == _end_of_storage)
    {
        //记录开始到pos的距离
        size_t n = pos - _start;
        //扩容
        size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newcapacity);
            
        //处理迭代器失效
        //传过来的pos是指向原来数组的,但是扩容以后原来的数组被释放掉了
        //需要将pos指向新的数组
        pos = _start + n;
    }
    //挪数据
    iterator end = _finish - 1;
    while (end >= pos) 
    {
        *(end + 1) = *end;
        end--;
    }
    //插入
    *pos = x;
    _finish++;
}

4.pos位置删除  iterator easer(iterator pos)

注意⚠️:删除会返回一个迭代器,并且返回的迭代器指向被删除的元素的下一个

iterator easer(iterator pos)
{
    assert(pos < _finish);
        
    //挪数据
    iterator it = pos;
    while (it < _finish) {
        *it = *(it + 1);
        it++;
    }
    //改
    _finish--;
        
    //返回被删元素的下一个的位置
    return pos;
}

(五)operator[]

1.普通对象使用

T& operator[](size_t i)
{
    assert(i < size());
    return _start[i];
}

2.const对象使用

const T& operator[](size_t i) const
{
    assert(i < size());
    return _start[i];
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值