C++——vector容器模拟实现

1、vector模拟实现

2、迭代器失效问题

3、深浅拷贝导致的程序崩溃问题分析


1、vector模拟实现

namespace vec1
{
    template <class T>
    class vector
    {
    public:
        typedef T* iterator;    //vector中的iterator迭代器可以是原生指针
        typedef const T* const_iterator    //用来访问const变量或函数的迭代器
        
        iterator begin()    //返回vector的头位置
        {
            return _start;
        }        

        iterator end()    //返回vector的尾位置
        {
            return _finish;
        }

        const_iterator begin() const    //返回vector的头位置,且返回值无法修改
        {
            return _start;
        }

        const_iterator end() const    //返回vector的尾位置,且返回值无法修改
        {
            return _finish;
        }

        vector()    //默认构造函数
            :_start(nullptr)
            ,_finish(nullptr)
            ,_end_of_storage(nullptr)
        {}

        vector(const vector<T>& v)    //拷贝构造函数
        {
            _start = new T[v.size()];
            //memcpy(_start, v._start, sizeof(T)*v.size());    //(涉及到深浅拷贝问题)
            for(size_t i = 0; i < v.size(); ++i)
            {
                _start[i] = v.start + v.size();
            }
            _finish = _start + v.size();
            _end_of_storage = _start + v.size();
        }

        vector(size_t n, const T& val = T())    //给n个值的半缺省构造函数
            :_start(nullptr)
            ,_finish(nullptr)
            ,_end_of_storage(nullptr)
        {
            reserve(n);    //扩容
            for(size_t i = 0; i < n; ++i)
            {
                push_back(val);
            }
        }

        template <class InputIterator>
        vector(InputIterator first, InputIterator last)    //利用两个迭代器来初始化,可以传                        
                                                           //任意数据类型的数据
            :_start(nullptr)
            ,_finish(nullptr)
            ,_end_of_storage(nullptr)
        {
            while(first != last)
            {
                push_back(*first);
                ++first;
            }
        }

        void swap(vector<T>& v)        //用于两个vector容器的交换
        {
            std::swap(_start, v.start);
            std::swap(_finish, v._finish);
            std::swap(_end_of_storage, v._end_of_storage);
        }

        vector(const vector<T>& v)    //拷贝构造函数,间接型的
            :_start(nullptr)
            ,_finish(nullptr)
            ,_end_of_storage(nullptr)
        {
            vector<T> tmp(v.begin,v.end());    //利用两个迭代器将v的数据拷贝给tmp
            swap<tmp>;        //再将tmp与要拷贝给的对象互换
        }

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

        size_t capacity() const    //返回容量大小
        {
            return _end_of_storage - _start;
        }

        const T& operator[](size_t pos) const    //重定义[]访问运算符
        {
            assert(pos < size());
            return _start[pos];
        }

        size_t size() const    //返回容器内数据个数
        {
            return _finish - _start;
        }

        void reserve(size_t n)
        {
            if(n > capacity())
            {
                size_t sz = size();    //提前记录下数据个数,方便下面更新(涉及到迭代器失效)
                T*tmp = new T[n];
                if(_start)
                {
                    //memcpy(tmp, _start, sizeof(T)*sz);    //同上面所说过的涉及到深浅拷贝
                    for(size_t i = 0; i < sz; ++i)
                    {
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }
                
                _start = tmp;
                _finish = _start + sz;
                _end_of_storage = _start + n;
            }
        }

        void resize(size_t n, const T& val = T())    //给容器空间重新赋予容量
        {
            if(n > capacity())
            {
                reserve(n);
            }

            if(n > size())    //如果新的值比原来的数据个数多,要给后面新开的容量赋初值
            {
                while (_finish < _start + n)
                {
                    *_finish = val;
                    ++finish;
                }
            }
            else    //如果个数比原来少,就要让尾位置前移
            {
                _finish = _start + n;
            }
        }

        iterator insert(iterator pos, const T& x)    //向容器内插入值(涉及到迭代器失效)
        {
            assert(pos >= _start);
            assert(pos <= _finish);

            if(_finish == _end_of_storage)    //检查容量是否已满
            {
                size_t len = pos - _start;
                reserve(capacity() == 0 ? 4 : capacity() * 2);
                pos = _start + len;
            }

            iteartor end = _finish - 1;    //将pos以及后面的值依次后移
            while (end >= pos)
            {
                *(end + 1) = *end;
                --end;
            }
            *pos = x;
            ++_finish;
            return pos;
        }

        void push_back(const T& x)    //尾插
        {
            insert(end(), x);
        }
           
        void pop_back()    //尾删
        {
            assert(_finish > _start);
            --_finish;
        }

        iterator erase(iterator pos)    //删除pos位置的值(涉及到迭代器失效问题)
        {
            assert(pos >= _start);
            assert(pos < _finish);
            iterator begin = pos + 1;
            while(begin < _finish)
            {   
                *(begin - 1) = *begin;
                ++begin;
            }
            --_finish;
            return pos;
        }

        private:
            iterator _start;
            iterator _finish;  
            iterator _end_of_storage;
    }
}

2、迭代器失效问题

导致迭代器失效的原因有两个:

1、扩容导致的野指针

2、pos指向位置已经不是原来的值了

插入扩容导致的迭代器失效:

void reserve(size_t n)        //扩容
        {
            if(n > capacity())
            {
                size_t sz = size();    //提前记录下数据个数,方便下面更新(涉及到迭代器失效)
                T*tmp = new T[n];
                if(_start)
                {
                    //memcpy(tmp, _start, sizeof(T)*sz);    //同上面所说过的涉及到深浅拷贝
                    for(size_t i = 0; i < sz; ++i)
                    {
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }
                
                _start = tmp;
                _finish = _start + sz;

                //_finish = _start + size();这是错误写法,会导致迭代器失效,_finish会变成野指针
                _end_of_storage = _start + n;
            }
        }

 pos指向位置已经不是原来的值了:

这里拿插入和删除各举一个例子:

要求在所有偶数前插入这个偶数本身的二倍:

auto it = v.begin();

while (it != end())

{

        if (*it % 2 == 0)

        v.insert(it, *it * 2);

        ++it;

}

这个写法看起来没有问题,但是实际运行的时候便会出现程序崩溃或者一直循环,看下图分析:

 所以正确的写法应该是:

auto it = v.begin();

while (it != end())

{

        if (*it % 2 == 0)

        {

        v.insert(it, *it * 2);

        ++it;

        ++it;

        }

        else

        ++it;

}

 删除也是同理:

要求删除所有的偶数:

auto it = v.begin();

while (it != v.end())

{

        if(*it %2 == 0)

        {

                v.erase(it);

        }

        ++it;

}

 正确写法应该是:

auto it = v.begin();

while (it != v.end())

{

        if(*it %2 == 0)

        {

               it = v.erase(it);

        }

        else

        ++it;

}

 


3、深浅拷贝导致的程序崩溃问题分析

深浅拷贝导致的程序崩溃本质上就是析构函数被执行了两次,第二次析构函数执行错误。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

双葉Souyou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值