STL之vector(讲解迭代器失效,拷贝构造函数等现代版写法)

还是老规矩,直接上代码:

#pragma once
#include "riterator.hpp"
#include <iostream>
#include <assert.h>
#include <set>
#include <map>
using namespace std;
namespace cc
{
    template<class T>
    class vector
    {
     public:
        typedef T* iterator;
        typedef const T* const_iterator;
        vector()
            : _start(new T[4])
            , _finish(_start)
            , _end_of_strag(_start+4)
        {}
        template<class Iterator>
        vector(Iterator begin,Iterator end)
            : _start(new T[4])
            , _finish(_start)
            , _end_of_strag(_start+4)
        {
            //同样,这里最好不要用memcpy等函数(涉及深浅拷贝问题)
            while(begin!=end)
            {
                push_back(*begin);
                begin++;
            }
        }
        vector(const vector<T>& d)
            : _start(nullptr)
            , _finish(nullptr)
            , _end_of_strag(nullptr)
        {
            vector<T> tem(d.begin(),d.end());
            swap(tem);
        }
        ~vector()
        {
            delete[] _start;
            _start=_finish=_end_of_strag=nullptr;
        }
        vector<T>& operator=(vector<T> d)
        {
            swap(d);
            return *this;
        }
        void swap(vector<T>& d)
        {
            std::swap(_start,d._start);
            std::swap(_finish,d._finish);
            std::swap(_end_of_strag,d._end_of_strag);
        }
        iterator begin()
        {
            return _start;
        }
        iterator end()
        {
            return _finish;
        }
        const_iterator begin()const
        {
            return _start;
        }
        const_iterator end()const
        {
            return _finish;
        }
        size_t size()const
        {
            return _finish-_start;
        }
        size_t capacity()const
        {
            return _end_of_strag-_start;
        }
        T& operator[](size_t x)const
        {
            assert(x<size());
            return _start[x];
        }
        void resize(size_t n,const T& x=T())
        {
            _finish=_start+n;
            for(size_t i=0;i<n;i++)
                _start[i]=x;
        }
        void reserve(size_t n)
        {
            if(n>size())
            {
                iterator tem=new T[n];
                //这里最好不要用memcpy等函数(涉及深浅拷贝问题)
                for(size_t i=0;i<size();i++)
                    tem[i]=_start[i];
                size_t ret=_finish-_start;
                delete[] _start;
                _start=tem;
                _end_of_strag=_start+n;
                _finish=_start+ret;
            }
        }
        void push_back(const T& x)
        {
            if(size()==capacity())
                reserve(2*capacity());
            _start[_finish-_start]=x;
            _finish++;
        }        
     private:
        iterator _start;
        iterator _finish;
        iterator _end_of_strag;  
    };
}

很简单的一个容器,其实 本质原理就是类似于顺序表,在这里我并没有实现所有的功能,但是主要的功能都已经实现,顺序表我想大家应该都是比较熟悉的,所以这里就不讲实现的原理了,来说说这里最主要的一个问题。

vector最主要的问题是什么呢?其实就是深浅拷贝和迭代器的失效。

1.深浅拷贝的问题

这个很容易就写错了,哪怕拷贝构造函数等写的是深拷贝,但是如果再reserve这个函数内部一不注意就会出现浅拷贝的问题,注释中已经说明,这里不再多说,这个还是比较简单的。如果不知道自己实现的vector是否完成深拷贝,可以自己建造一个二维数组来试试

2.迭代器失效

迭代器失效在vector中有大两种:分别是插入,删除

插入导致迭代器失效:插入其实又有两种迭代器失效的可能,一种是插入的时候,需要扩容,此时如果一不小心就会到直接迭代器直接失效,其次就是在任意地方插入的时候,会导致迭代器失效的可能。

扩容导致迭代器失效原理:

我们可以看看下面的代码:

     //正确版
        // void reserve(size_t n)
        // {
        //     if(n>size())
        //     {
        //         iterator tem=new T[n];
        //         //这里最好不要用memcpy等函数(涉及深浅拷贝问题)
        //         for(size_t i=0;i<size();i++)
        //             tem[i]=_start[i];
        //         size_t ret=_finish-_start;
        //         delete[] _start;
        //         _start=tem;
        //         _end_of_strag=_start+n;
        //         _finish=_start+ret;
        //     }
        // }
        //错误版
        void reserve(size_t n)
        {
            if(n>size())
            {
                iterator tem=new T[n];
                for(size_t i=0;i<size();i++)
                    tem[i]=_start[i];
                delete[] _start;
                _start=tem;
                //经典错误出现的地方
                _finish=_start+size();
                _end_of_strag=_start+n;
            }
        }

为什么说这里有错误呢?两个看起来其实感觉也差不多,但为什么下面的是错误的呢?其实很好理解,因为在这里你的_finish=_start+size(),这里是不能加size()的,因为size的内部实现返回的是__finish-_start,但是此时的_start指向的是tem的空间,并且把原来的空间已经给释放了,所以此时会出现问题。

其次就是任意位置插入会导致迭代器失效原理:如下图:

 我们可以看到的是,我在3这里插入了7,把所有的数后移了一位,但是,我们可以看到的是,此时原本3这个位置的数变成了7,但是迭代器的位置其实没有变,所以此时你如果不更新迭代器的话,就到导致迭代器的失效。

删除导致迭代器的失效:

原理其实和插入差不多,都是原本位置上的数改变,而迭代器的位置没有改变,所以此时要更新迭代器,原理和插入导致的迭代器失效其实是一样的。大家可以仔细想想

还有就是拷贝构造和赋值运算符的重载的现代版写法,其实个人建议的是,如果理解不了现代版的写法,可以按照深拷贝的思路来慢慢写,现代版的实现只不过是代码比较简洁而已,不过还是看个人喜好了,现代版的实现提高了成员函数的耦合性(个人认为不好的一点),如果出现错误的,维修成本比较大,而原始的写法就没有这种问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值