C++学习指南(五)——vector

欢迎来到繁星的CSDN。本期内容主要包括vector的实现与功能。

一、什么是vector?

        vector可以说,打开了STL库的序章。

        作为最常用、最普适、最普遍的一个容器,vector会出现在程序的各个角落。

        而我们初期,可以理解为原先的数组,两者均为连续存储,在进行过函数重载和运算符重载后,实际上的操作也相差不大。

#include<iostream>
#include<vector>
using namespace std;

int main(){
    vector<int> arr(10,0);
    for(int i=0;i<arr.size(),i++){
        arr[i]=i;
        cout<<arr[i]<<endl;
    }

    return 0;
}

 二、vector的功能/函数

        vector相对于string这一最初始的封装而言,在设计更有章法和逻辑思维,不再有大量重复,或是无用的重载函数出现。

        老规矩,先上文档网址

        cplusplus.com/reference/vector/vector/

        2.1vector的构造

    vector();//无参构造
    vector(size_type n,const value_type& val =value_type());//构造并初始化n个val
    vector(const vector&x);//拷贝构造
    vector(inputiterator first,inputiterator last);//使用迭代器进行初始化构造

        这四个均为vector的构造方式,为了适应不同情况而创造出来。

       2.2vector iterator的使用方式

        之前提到过,iterator的出现,实现了多种结构的封装。大部分的容器,string、vector、list、map等,都可以用iterator进行遍历、构造等操作,这正是iterator的意义所在。

        iterator在每个容器里的使用方式相同,减少了大量记忆成本。

   begin+end,正向迭代器使用

   rbegin+rend,反向迭代器使用

   但需要注意的是,begin和rbegin分别取的是正向和反向的第一个元素,但end是最后一个元素的下一个位置,rend是第一个元素的前一个位置。

       2.3vector的容量

        vector的容量操作相比string而言简单很多,主要使用的也只有如下函数:

    size();
    capacity();
    empty();
    resize(size_t n, value_type val=value_type());
    reserve(size_t n);

        操作和string的操作完全相同,这里就不过多介绍了。

        不过值得注意的是,vector里的数据类型可以是string、vector等等,所以resize里的val可能是各种类型的值,这需要开发者自己注意。

        另,capacity的增长不一定是2倍,vs平台的增长速度是1.5倍,而G++平台的增长速度则是2倍,这取决于STL库的版本。故,不要固化地认为增长一定是2倍增长。

       2.4vector的增删查改

        vector的增删查改方式如下:

void push_back(value_type val);
void pop_back();
inputiterator find(inputiterator first, inputiterator last, const T& val);
iterator insert (iterator position, const value_type& val);
    void insert (iterator position, size_type n, const value_type& val);	
    template <class InputIterator>    void insert (iterator position, InputIterator first, InputIterator last);
iterator erase (iterator position);
    iterator erase (iterator first, iterator last);
reference operator[] (size_type n);
    const_reference operator[] (size_type n) const;
void swap (vector& x);

        push_back,pop_back,swap,erase,[ ]都是老朋友了,用法也大致相同。

        find函数不是vector的成员函数,而是算法实现的,所以只包vector的头文件,将无法使用find。

        insert函数的用法比较多,也显得会比较方便。

        erase也支持了擦除一个数据,擦除一个区间内的数据的用法。

       总体而言,vector的STL库还是令人满意的。

三、vector的迭代器失效问题

        迭代器的封装使得我们可以忽略底层实现,而直接在各种容器中使用,但实际上底层仍然是指针。

        很可惜的是,迭代器存在失效的情况。

       扩容时

        我们观察以下代码:
        

    vector<int>v{1,2,3,4,5,6};
    v.push_back(4);
    v.resize(100,4);
    v.insert(4,5);
    v.assign(100,8);(将v替换为100个8)
    

        每一行代码都有可能出现迭代器失效的问题。

        不知是否还记得学习C语言的时候,我们用realloc的时候曾经说过,一旦连续的内存容量不够,realloc将重新找一个足够巨大的空间扩容。

        而此时的迭代器还在原先的位置,自然发生了无效访问,且一定会报错(访问已释放的空间)

        

      erase时

        我们之前特别提到了,迭代器的end和rend所指代的位置都是没有数据的。

        所以当我们消除的是最后一个元素的时候,pos的位置变成了end(因为erase的时候end也要前移),迭代器失效。

       如何解决这个问题?

        在VS中,面对任何可能出现迭代器失效的情况,都会直接暴力报错,这有效地避免了我们的误操作。

        至于如何解决这个问题,我们只需要在迭代器失效时,及时更新迭代器即可。(重新赋值)

        

四、vector的模拟实现

        切勿当作源码看,如需源码,详情还请看《STL源码剖析》

#define  _CRT_SECURE_NO_WARNINGS
#include<iostream>

namespace show {
	template <class T>
    class vector {
    public:

        // Vector的迭代器是一个原生指针

        typedef T* iterator;
        typedef const T* const_iterator;

        iterator begin() {
            return _start;
        }

        iterator end() {
            return _finish;
        }

        const_iterator cbegin() {
            return _start;
        }

        const_iterator cend() const {
            return _finish;
        }



        // construct and destroy

        vector()=default;
        
        vector(int n, const T& value = T()) {
            reserve(n);
            for (int i = 0; i < n; i++) {
                push_back(value);
            }
        }

        template<class InputIterator>

        vector(InputIterator first, InputIterator last) {
            while (first != last) {
                push_back(*first);
                first++;
            }
        }

        vector(const vector<T>& v) {
            reserve(v.size());
            for (auto e : v) {
                push_back(e);
            }
        }

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

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

         // capacity

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

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

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

         void resize(size_t n, const T& value = T()) {
             if (n < size())
             {
                 _finish = _start + n;
             }
             else
             {
                 reserve(n);
                 while (_finish < _start + n)
                 {
                     *_finish = value;
                     ++_finish;
                 }
             }
         }



         ///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 (_finish = _endOfStorage) {
                reserve(capacity() == 0 ? 4 : capacity() * 2);
            }
            *_finish = x;
            ++_finish;
        }

        void pop_back() {
            --_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) {
            if (_finish == _end_of_storage)
            {
                size_t len = pos - _start;
                reserve(capacity() == 0 ? 4 : capacity() * 2);
                pos = _start + len;
            }

            iterator end = _finish - 1;
            while (end >= pos)
            {
                *(end + 1) = *end;
                --end;
            }
            *pos = x;

            ++_finish;

            return pos;
        }

        iterator erase(iterator pos) {
            iterator it = pos + 1;
            while (it != end())
            {
                *(it - 1) = *it;
                ++it;
            }

            --_finish;
        }

    private:

        iterator _start=nullptr; // 指向数据块的开始

        iterator _finish=nullptr; // 指向有效数据的尾

        iterator _endOfStorage=nullptr; // 指向存储容量的尾
	};
}

        本篇内容到此结束,谢谢大家的观看!

        觉得写的还不错的可以点点关注,收藏和赞,一键三连。

        我们下期再见~

        往期栏目:

        C++学习指南(一)——C++入门基础_c++ 学习指南-CSDN博客

        C++学习指南(二)——类和对象-CSDN博客

        C++学习指南(三)——模板_模板类-CSDN博客 

        C++学习指南(四)------string-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值