vector 4个月前

编程语言
453
vector

一、vector的注意事项

1. [] 和 at() 的越界访问

vector<int> v1{ 1,2,3,4 };

    //v1[4] = 5; //越界直接结束,assert判断

    try
    {
        v1.at(4) = 5; //抛异常
    }
    catch (...)
    {
        cout << "beyond" << endl;  //输出beyond
    }

2.迭代器失效问题

简单来说就是在迭代器创建后 使用了reverse,resize,insert,push_back等导致的增容 导致迭代器仍然指向已经被释放的旧空间

一个应对迭代器失效的过程

//删除容器中所有的偶数
    vector<int>::iterator it = v.begin();
    while (it != v.end())
    {
        if (*it % 2 == 0)
        {
            v.erase(it); 
        }
        //然后该怎么++it?
        
        ++it;  //此时会报错,为什么?
                //erase删除一个数据后,其他数据向前移动,移动后其实it已经指向我们想要遍历的下一个元素
                //此时再++it就会导致错过部分数据

        else
        {
            ++it;  //那么这样处理可以么?
                   //Linux环境下这样可以达到目的,但是vs编译环境自动检查且较为严格,仍然报错
        }
    }
    
    
    //正确做法
    vector<int>::iterator it2 = v.begin();
    while (it2 != v.end())
    {
        if (*it2 % 2 == 0)
            it2 = v.erase(it2); //正确做法 erase会返回删除的it的下一个位置的迭代器
        else
            ++it2;  
    }

二、vector的模拟实现

有以下要点:

reserve中浅拷贝memmove问题 新型拷贝构造和重载赋值符写法 insert时的迭代器失效

namespace 9TSe
{
    template<class T>
    class vector
    {
    public:
        typedef T* iterator; 
        typedef const T* const_iterator;
        vector()
            :_start(nullptr)
            ,_finish(nullptr)
            ,_endofstorage(nullptr)
        {}

        //v2(v1)
        //vector(const vector<T>& v)
        //{
        //    _start = new T[v.capacity()];     //开辟新空间
        //    _finish = _start;
        //    _endofstorage = _start + capacity();

        //    for (size_t i = 0; i < v.size(); ++i)
        //    {
        //        *_finish = v[i];
        //        ++_finish;
        //    }
        //}

        //拷贝构造的另一种方法
        vector(const vector<T>& v)
            :_start(nullptr)
            ,_finish(nullptr)
            ,_endofstorage(nullptr)
        {
            for (auto& e : v)
            {
                reserve(v.capacity());
                push_back(e);
            }
        }

        //vector<T>& operator=(const vector<T>& v)
        //{
        //    if (*this != &v) //防止自己赋值给自己
        //    {
        //        delete[] _start; //s1 = s2 s1有自己的空间,先释放
        //        _start = new T[v.capacity()];

        //        memcpy(_start, v._start, sizeof(T) * v.size());
        //    }
        //    return *this;
        //}

        //重载赋值运算符简便写法
        void swap(vector<T>& v)
        {
            ::swap(_start, v._start); 
            ::swap(_finish, v._finish);
            ::swap(_endofstorage, v._endofstorage);
        }

        vector<T>& operator=(vector<T> v)
        {
            if (*this != &v)
                swap(v);        //这里不用库内自带的swap是因为,其会生成三个深拷贝,有很大的代价
            return *this;
        }

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

        void reserve(size_t n)
        {
            if (n > capacity()) //增大容量时
            {
                size_t sz = size();
                T* tmp = new T[n];
                if (_start) //这里的用意是防止_start为空指针时扩容,会导致memcpy出错(memcpy内不能存在空指针)
                {
                    //memcpy(tmp, _start, sizeof(T) * size()); //这个memcpy是浅拷贝,当T为string时,会导致指针指向同一位置,释放两次同一空间
                    for (size_t i = 0; i < sz; ++i)
                    {
                        tmp[i] = _start[i];
                    }
                    delete[] _start; 
                }
                _start = tmp;
                _finish = tmp + sz; //如果这里赋予 tmp + size() 会出错 
                                    //sz的计算方式是_finish-_start
                                    //_start已经指向新空间了,finish还指向旧空间
                _endofstorage = tmp + n;//这里如果赋予tmp + capacity() 也会出错,与上同理
            }
            
        }

        void resize(size_t n, const T& v = T()) //这里的T()为T类型的默认缺省值,有int() double() 等都有
                                       //int() double()等内置类型,只是专门创建了此形式的东西
                                       //string ,vector 是调用构造函数
        {
            if (n < size()) //缩容
            {
                _finish = _start + n;
            }
            else
            {
                if (n > capacity())
                {
                    reserve(n);
                }

                while (_finish < _start + n)
                {
                    *_finish = v;
                    ++_finish;
                }
            }
        }

        void push_back(const T& x)
        {
            if (_finish == _endofstorage) //扩容
            {
                size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
                reserve(newcapacity);
            }
            *_finish = x;
            ++_finish;
        }


        void insert(iterator pos, const T& v) //这里会有迭代器失效问题
        {
            assert(pos <= _finish);
            if (_finish == _endofstorage)//pos会扩容后失效
            {
                size_t n = pos - _start; //因此要记录之间的距离,以明确新pos指针指向的位置

                size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
                reserve(newcapacity);

                pos = _start + n; //设置新pos
            }

            iterator end = _finish - 1;
            while (end >= pos)   //移动数据
            {
                *(end + 1) = *end;
                end--;
            }
            *pos = v;
            ++_finish;
        }

        iterator erase(iterator pos)
        {
            assert(pos < _finish&& _start != _finish);
            iterator it = pos;
            while (it < _finish)
            {
                *it = *(it + 1);
                ++it;
            }
            _finish--;
            return pos;
        }


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

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

        iterator end()
        {
            return _finish;
        }

        iterator begin()
        {
            return _start;
        }

        const_iterator end()const
        {
            return _finish;
        }

        const_iterator begin()const
        {
            return _start;
        }

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

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

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

    private:
        iterator _start;
        iterator _finish; //这个指针指向的是最后一个数据的下一个数据的位置
        iterator _endofstorage;
    };
}
image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2223151
累计阅读

热门教程文档

Flutter
105小节
React Native
40小节
Golang
23小节
PHP
52小节
Linux
51小节