vector的模拟实现

目录

一、vector的核心框架(成员介绍)

二、迭代器的模拟实现

三、默认成员函数

1、无参构造

2、由n个值为 x 的元素构造一个vector

3、用一段迭代器区间进行构造

4、拷贝构造

5、赋值运算符重载

6、析构函数

四、容量相关的接口

1、数据个数(size)

2、容量大小(capacity)

3、容量增大至n,不可缩容(reserve)

4、修改vector中的数据数量大小(resize)

五、支持随机访问的接口(运算符[]的重载)

六、修改元素的接口

1、在(迭代器)pos位置插入一个元素,并返回指向它的迭代器(insert)

2、尾插(push_back)

3、删除(迭代器)pos位置的一个元素并返回被删元素的下一个位置的迭代器(erase)

4、尾删

代码总结


一、vector的核心框架(成员介绍)

在STL之中的vector中,是由三个指针来控制数据的个数以及容量的大小的!

start指针:这个指针指向的是数据的开始位置

finish指针:这个指针指向的是最后一个数据的下一个位置

endOfStorage指针:这个指针指向的是vector所开辟的空间中的最后一个位置

如图所示

 而通过这三个指针,我们就能控制数据的个数以及容量的大小

数据的个数:finish指针-start指针就是数据的个数

容量的大小:endOfStorage指针-start指针就是容量的大小

代码如下

size_t capacity()
{
    return _endOfStorage - _start;
}
size_t size()
{
    return _finish - _start;
}

以上就是vector的核心框架,那么下面就让我们来模拟实现一下

二、迭代器的模拟实现

关于vector的迭代器其实就是一个模板类型指针实现的,因为vector是连续存储数据的,所以不需要对迭代器另外实现,原生指针就可满足需求

 而这个迭代器的开始是start指针,结束是finish指针

template<class T>
class vector
{
 public:
    typedef T* iterator;
    typedef const T* const_iterator;
 private:
    iterator _start;
    iterator _finish;
    iterator _endOfStorage;
 public:
    //迭代器
    iterator begin()
    {
        return _start;
    }
    const_iterator cbegin()const
    {
        return _start
    }
    iterator end()
    {
        return _finish;
    }
    const_iterator end()const
    {
        return _finish;
    }
};

三、默认成员函数

在写构造函数之前我们先把成员的缺省值设置为空

template<class T>
class vector
{
    //...
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _endOfStorage = nullptr;
};

1、无参构造

template<class T>
class vector
{
    //...
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _endOfStorage = nullptr;
    public:
        vector()
        {}
};

无参构造不需要我们显示写,编译器会自动走初始化列表把缺省值赋上

2、由n个值为 x 的元素构造一个vector

我们需要先开辟出一个足够大的空间,再把值赋上即可

template<class T>
class vector
{
    //...
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _endOfStorage = nullptr;
    public:
        vector(size_t n , const T& x)
        {
           _start = new T[n];
           for(size_t i = 0 ; i < n ; ++i)
           {
                *(_start + i) = x;
           }
           _finish = _endOfStorage = _start + n;
        }
};

3、用一段迭代器区间进行构造

template<class T>
class vector
{
    //...
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _endOfStorage = nullptr;
    public:
    //此模板类型用于接收迭代器区间
    template<class InputIterator>
    vector(InputIterator first,InputIterator last)
    {
        while(first != last)
        {
            push_back(*first);//此函数是尾插,也就是把数据插入到vector的尾部,后面会实现
            ++first;
        }
    }
    
};

但是此处的迭代器区间构造还有一些问题

思考一下:当我们使用"vector<int> v(8,0)"时会走哪一个构造呢?

'8'和'0'都是int类型的整形,而前面的vector(size_t n,const T& x); n是size_t 类型,编译器不会自动走这个类型,而会走迭代器区间构造这个构造函数

解决办法一、把前面的vector(size_t n , const T& x)改为vector(int n , const T& x);(不推荐)

解决办法二、我们使用函数重载,重新写一个更符合(int,int)类型的函数

template<class T>
class vector
{
    //...
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _endOfStorage = nullptr;
    public:
    //此模板类型用于接收迭代器区间
    template<class InputIterator>
    vector(InputIterator first,InputIterator last)
    {
        while(first != last)
        {
            push_back(*first);//此函数是尾插,也就是把数据插入到vector的尾部,后面会实现
            ++first;
        }
    }
    vector(int n , int x)
    {
        _start = new T[n];
        for(int i = 0 ; i < n ; ++i)
        {
            *(_start + i) = x;
        }
        _finish = _endOfStorage = _start + n;
    }
};

4、拷贝构造

传统写法:关于拷贝构造我们需要做的动作有两个,第一个是重新开一个一样大的空间(避免浅拷贝),第二是把原数据拷贝给新对象(当然还要考虑自己拷贝自己的情况)

 注意:此处拷贝不能用memcpy,因为memcpy是按字节序进行拷贝,如果这里的模板是内置类型,那么memcpy不会出问题,但如果是list,Date,string这类自定义类型,按字节序进行拷贝就会出现浅拷贝问题,浅拷贝问题在string时说过!,所以这里我们需要一个一个进行拷贝!

template<class T>
class vector
{
    //...
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _endOfStorage = nullptr;
    public:
        vector(const vector<T>& v)
        {
         if(v != *this)
         {
            //1、开空间
            _start = new T[v.size()];
            //2、赋值
            for(size_t i = 0 ; i < v.size() ; ++i)
            {
                *(_start + i) = *(v._start + i);
            }
            _finish = _start + v.size();
            _endOfStorage = _start + v.capacity();
         }
        }

};

现代写法:可以直接复用迭代器区间构造,因为传过来的类型也是vector,而既然是vector就有迭代器区间构造,然后我们再写一个交换函数,交换两个vector对象,就实现了深拷贝

代码如下

template<class T>
class vector
{
    //...
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _endOfStorage = nullptr;
    public:
        void swap(vector<T>& v)
        {
            std::swap(_start,v._start);
            std::swap(_finish,v._finish);
            std::swap(_endOfStorage,v._endOfStorage);
        }
        vector(const vector<T>& v)
        {
            if(*this != v)
            {
                vector<T> tmp(v.begin(),v.end());
                swap(tmp);
            }
        }

};

5、赋值运算符重载

与拷贝构造很类似,这里不再过多说明传统写法,而直接使用现代写法

代码如下:

vector<T>& operator= (vector<T> v)
{
    swap(v);
    return *this;
}

6、析构函数

析构函数没有什么特别的,直接把创建的数组delete掉即可

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

四、容量相关的接口

1、数据个数(size)

size_t size()
{
    return _finish - _start;
}

2、容量大小(capacity)

size_t capacity()
{
    return _endOfStorage - _start;
}

3、容量增大至n,不可缩容(reserve)

第一步:新增一个T* 的指针,并开n个空间

第二步:把原数据拷贝至新数组中

第三步:释放原数组

注意:因为我们要释放原数组,而当我们释放完原数组后要把三个成员指针重新赋值,这时我们就需要有size和capacity,由于size和capacity是由三个指针控制的,但此时size和capacity都失效了,因为已知容量是n,那么我们就不需要capacity,我们只需要提前记录一下size即可

void reserve(size_t n)
{
    
    if(n > capacity())
    {
        size_t sz = size();
        T* tmp = new T[n];
        for(size_t i = 0 ; i < sz ; ++i)
        {
            *(tmp + i) = *(_start + i);
        }
        delete[]_start;
        _start = tmp;
        _finish = _start + sz;
        _endOfstorage = _start + n;
    }
}

4、修改vector中的数据数量大小(resize)

在我们模拟实现这个函数之前先了解一下这个接口

1、这个接口的主要作用是用于修改size的大小

2、如果修改的size>capacity,则这个接口也会实现扩容

3、如果修改的size小于当前的size,则会把size减少到修改后的size,并把多出来的元素删除

4、当修改后的size大于当前的size时,这个接口会把size增大为修改后的size,并插入指定的元素值(默认为初始化)

既然我们了解了这些,接下来就让我们来进行模拟实现

void resize(size_t n , const T& x = T())
{
    //因为后面要修改finish指针,size是由finish指针计算得来的
    //所以我们提前记录下size的大小
    size_t sz = size();
    if (n > capacity())
    {
        reserve(n);
    }
    _finish = _start + n;
    if (n > sz)
    {
      for (size_t i = sz; i < n; ++i)
      {
         *(_start + i) = x;
      }
    }
}

五、支持随机访问的接口(运算符[]的重载)

T &operator[](size_t n)
{
    return *(_start + n);
}
const T &operator[](size_t n)const
{
    return *(_start + n);
}

六、修改元素的接口

1、在(迭代器)pos位置插入一个元素,并返回指向它的迭代器(insert)

注意:因为我们的reserve扩容函数中是把原数组给释放掉了,而pos迭代器指向的还是原数组也就导致pos成为野指针,所以我们需要提前计算好pos的位置,在扩容完成之后把他重新指向新数组的对应位置

iterator insert(iterator pos,const T& x)
{
        if(size() == capacity())
        {
            //计算pos在扩容完后该指向新数组的哪个位置
            size_t len = pos - _start;
            size_t newcapacity = capacity() == 0 ? 10 : 2*capacity();
            reserve(newcapacity);
            pos = _start + len;
        }
        iterator cur = _finish;
        while(cur != pos)
        {
            *cur = *(cur - 1);
            --cur;
        }
        *pos = x;
        ++_finish;
        return pos;
}

2、尾插(push_back)

尾插直接复用insert即可

void push_back(const T& x)
{
        insert(_finish,x);
}

3、删除(迭代器)pos位置的一个元素并返回被删元素的下一个位置的迭代器(erase)

iterator erase(iterator pos)
{
        iterator cur = pos + 1;
        while(cur != _finish)
        {
            *(cur-1) = *cur;
            ++cur;
        }
        --_finish;
        return pos;
}

4、尾删

void pop_back()
{
        erase(_finish - 1);
}

至此一个支持简单增删查改的vector容器也就实现了!

代码总结

template <class T>
class vector
{
public:
    typedef T *iterator;
    typedef const T *const_iterator;

private:
    iterator _start = nullptr;
    iterator _finish = nullptr;
    iterator _endOfStorage = nullptr;

public:
    // 迭代器
    iterator begin()
    {
        return _start;
    }
    const_iterator cbegin() const
    {
        return _start;
    }
    iterator end()
    {
        return _finish;
    }
    const_iterator cend() const
    {
        return _finish;
    }
    vector()
    {
    }

    vector(size_t n, const T &x)
    {
        _start = new T[n];
        for (size_t i = 0; i < n; ++i)
        {
            *(_start + i) = x;
        }
        _finish = _endOfStorage = _start + n;
    }

    template <class InputIterator>
    vector(InputIterator first, InputIterator last)
    {
        while (first != last)
        {
            push_back(*first); // 此函数是尾插,也就是把数据插入到vector的尾部,后面会实现
            ++first;
        }
    }
    vector(int n, int x)
    {
        _start = new T[n];
        for (int i = 0; i < n; ++i)
        {
            *(_start + i) = x;
        }
        _finish = _endOfStorage = _start + n;
    }

    // vector(const vector<T> &v)
    // {
    //     if (v != *this)
    //     {
    //         // 1、开空间
    //         _start = new T[v.size()];
    //         // 2、赋值
    //         for (size_t i = 0; i < v.size(); ++i)
    //         {
    //             *(_start + i) = *(v._start + i);
    //         }
    //         _finish = _start + v.size();
    //         _endOfStorage = _start + v.capacity();
    //     }
    // }

    void swap(vector<T> &v)
    {
        std::swap(_start, v._start);
        std::swap(_finish, v._finish);
        std::swap(_endOfStorage, v._endOfStorage);
    }
    vector(const vector<T> &v)
    {
        if (_start != v._start)
        {
            vector<T> tmp(v.cbegin(), v.cend());
            swap(tmp);
        }
    }
    vector<T> &operator=(vector<T> v)
    {
        swap(v);
        return *this;
    }

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

    size_t size()
    {
        return _finish - _start;
    }

    size_t capacity()
    {
        return _endOfStorage - _start;
    }

    void reserve(size_t n)
    {

        if (n > capacity())
        {
            size_t sz = size();
            T *tmp = new T[n];
            for (size_t i = 0; i < sz; ++i)
            {
                *(tmp + i) = *(_start + i);
            }
            delete[] _start;
            _start = tmp;
            _finish = _start + sz;
            _endOfStorage = _start + n;
        }
    }

    void resize(size_t n, const T &x = T())
    {
        size_t sz = size();
        if (n > capacity())
        {
            reserve(n);
        }
        if (n > sz)
        {
            for (size_t i = sz; i < n; ++i)
            {
                *(_start + i) = x;
            }
        }
    }
    T &operator[](size_t n)
    {
        return *(_start + n);
    }
    const T &operator[](size_t n) const
    {
        return *(_start + n);
    }

    iterator insert(iterator pos, const T &x)
    {
        if (size() == capacity())
        {
            size_t len = pos - _start;
            size_t newcapacity = capacity() == 0 ? 10 : 2 * capacity();
            reserve(newcapacity);
            pos = _start + len;
        }
        iterator cur = _finish;
        while (cur != pos)
        {
            *cur = *(cur - 1);
            --cur;
        }
        *pos = x;
        ++_finish;
        return pos;
    }
    iterator erase(iterator pos)
    {
        iterator cur = pos + 1;
        while (cur != _finish)
        {
            *(cur - 1) = *cur;
            ++cur;
        }
        --_finish;
        return pos;
    }
    void push_back(const T &x)
    {
        insert(_finish, x);
    }
    void pop_back()
    {
        erase(_finish - 1);
    }
};

                这期vector的模拟实现就到这啦,感谢你的支持,我们下期再见~~

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值