vector模拟实现——关于模拟中的易错点

前言

vector 本质上类似数组,也可以理解为一种泛型的 string。string 只能存储 char 类型,但是 vector 支持各种内置类型和自定义类型。本次将围绕模拟实现 vector 中遇到的问题进行分析。

一、确定思路

同 string 一样,在进行模拟之前我们要确定 vector 的结构是怎么样的,以及有哪些函数可用。

在这里插入图片描述

由图中我们可以知道 vector 所需要的函数相对于 string 较少,这主要是因为 vector 更支持泛型,因此类似于 string 的字典序比较等都不需要实现,毕竟不是每一个类型都是字典序比较。

下面给出一个 vector 的实现框架。

namespace hty{
	template<class T>
    class vector {
    private:
        typedef T* iterator;
        typedef const T* const_iterator;
        iterator _start; // 指向数据块的开始
        iterator _finish; // 指向有效数据的尾
        iterator _endOfStorage; // 指向存储容量的尾

    public:
        // iterator

        iterator begin() {
        }

        iterator end() {
        }

        const_iterator cbegin() const {
        }

        const_iterator cend() const {
        }
        
        
        // construct and destroy

        vector() {}
        
        vector(int n, const T& value = T()) {
        }
        
        vector(size_t n, const T& value = T()) {
        }

        template<class InputIterator>
        vector(InputIterator first, InputIterator last) {
        }

        vector(const vector<T>& v) {
        }

        vector<T>& operator= (vector<T> v) {
        }

        ~vector() {
        }
        

        // capacity

        size_t size() const {
        }

        size_t capacity() const {
        }

        bool empty() {
        }

        void clear() {
        }

        void reserve(size_t n) {
        }

        void resize(size_t n, const T& value = T()) {
        }
        
        
        //access

        T& operator[](size_t pos) {
        }
        
        const T& operator[](size_t pos)const {
        }
        
        
        // modify

        void push_back(const T& x) {
        }

        void pop_back() {
        }

        void swap(vector<T>& v) {
        }

        iterator insert(iterator pos, const T& x) {
        }

        iterator erase(iterator pos) {
        }
    };
}

二、实现过程

2.1 查阅文档

和模拟 string 时一样,模拟最重要的一步还是查阅文档,了解函数参数和功能。例如之前模拟实现 string 时,模拟 insert 函数传参是插入位置的下标,但是在 vector 中我们则传入的是迭代器。诸此之类的细节都需要在查阅文档后了解。

2.2 验证正确性

比起 string,vector 更需要对正确性的验证。虽然看似两者功能相近,但是由于 vector 更多时候面向的是自定义类型,因此更容易出现错误,而此类错误往往是连锁性的。

三、易错问题

在模拟实现过程中有两个常见的迭代器失效问题,这两处错误非常容易出现,分别由 insert 和 erase 引起。

3.1 insert 导致迭代器失效

insert 导致迭代器失效的原因在扩容。当我们对一个迭代器所指向位置插入数据时,其余数据向后移动。在原本剩余空间充足的情况下迭代器正常,但是如果剩余空间不够,需要扩容时,很多人容易忽略原 pos 迭代器会失效,具体失效原理如下:

在这里插入图片描述

3.2 erase 导致迭代器失效

erase 不存在扩容等改变原数组空间分配的情况,乍一看似乎不容易出现错误,但是在 string 中我们提到要注意特殊情况的正确性验证。如果我们删除的是最后一个位置的元素,那么之后 pos 迭代器指向的就是一块没有意义的地址。对于 vs 等迭代器非原生指针实现的情况,如果检查严格,就会中断报错。具体失效原理如下:

在这里插入图片描述

3.3 浅拷贝问题

不同于 string,vector 更多面向自定义类型。类似于重载 = 运算符,string 可以直接调用 memcpy 进行实现,但是对于 vector 则不行,vector 内容中也可能包含指针,需要自行开辟空间。最经典的例子就是 vector<vector>,因此在实现过程中,许多原本在 string 中 memcpy 可以解决的问题,我们需要用循环 + new 进行实现。这更体现了 C++ new 用于实现类型初始化的特性。

四、完整代码

namespace hty {
    template<class T>
    class vector {
    private:
        typedef T* iterator;
        typedef const T* const_iterator;
        iterator _start; // 指向数据块的开始
        iterator _finish; // 指向有效数据的尾
        iterator _endOfStorage; // 指向存储容量的尾

    public:
        // iterator

        iterator begin() {
            return _start;
        }

        iterator end() {
            return _finish;
        }

        const_iterator cbegin() const {
            return _start;
        }

        const_iterator cend() const {
            return _finish;
        }
        
        
        // construct and destroy

        vector() : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {}
        
        vector(int n, const T& value = T()) : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {
            resize(n, value);
        }
        
        vector(size_t n, const T& value = T()) : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {
            resize(n, value);
        }

        template<class InputIterator>
        vector(InputIterator first, InputIterator last) : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {
            while (first != last) {
                push_back(*first);
                first++;
            }
        }

        vector(const vector<T>& v) {
            vector<T> tmp(v.begin(), v.cend());
            swap(v);
        }

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

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

        // capacity

        size_t size() const {
            return _finish - _start;
        }

        size_t capacity() const {
            return _endOfStorage - _start;
        }

        bool empty() {
            return _start == _finish;
        }

        void clear() {
            _finish = _start;
        }

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

        void resize(size_t n, const T& value = T()) {
            if (n > capacity())
                reserve(n);
            if (n > size()) {
                for (int i = size(); i < n; i++)
                    _start[i] = value;
            }
            _finish = _start + n;
        }



        
        
        //access

        T& operator[](size_t pos) {
            return _start + pos;
        }
        
        const T& operator[](size_t pos)const {
            return _start + pos;
        }
        
        
        // modify

        void push_back(const T& x) {
            if (size() == capacity()) {
                size_t new_capacity = capacity() == 0 ? 4 : 2 * capacity();
                reserve(new_capacity);
            }
            *_finish = x;
            _finish++;
        }

        void pop_back() {
            assert(_finish > _start);
            --_finish;
        }

        void swap(vector<T>& v) {
            std::swap(_start, v._start);
            std::swap(_finish, v._finish);
            std::swap(_endOfStorage, v._endOfStorage);
        }

        iterator insert(iterator pos, const T& x) {
            assert(pos >= _start && pos <= _finish);
            if (size() == capacity()) {
                size_t len = pos - _start;
                size_t new_capacity = capacity() == 0 ? 4 : 2 * capacity();
                reserve(new_capacity);
                pos = _star + len;
            }
            for (auto i = _finish; i > pos; i--)
                *i = *(i - 1);
            *pos = x;
            _finish++;
            return pos;
        }

        iterator erase(iterator pos) {
            assert(pos >= _start && pos < _finish);
            for (auto x = pos; x < _finish - 1; x++)
                *x = *(x + 1);
            --_finish;
            return pos;
        }
    };
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值