C++stl库中vector类的解析和模拟实现

发布于:2025-02-11 ⋅ 阅读:(67) ⋅ 点赞:(0)

1.c++stl库

目录

1.c++stl库vector的介绍及使用

1.1vector的介绍

1.2vector的使用

1.2.1vector的定义

1.2.2vector iterator的使用

1.2.4vector增删查改

 1.2.5vector迭代器失效问题(重点)

2.vector的模拟实现源码


vector的介绍及使用

1.1vector的介绍

1. vector 是表示可变大小数组的序列容器。
2. 就像数组一样, vector 也采用的连续存储空间来存储元素。也就是意味着可以采用下标对 vector 的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
3. 本质讲, vector 使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector 并不会每次都重新分配大小。
4. vector 分配空间策略: vector 会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
5. 因此, vector 占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
6. 与其它动态序列容器相比( deque, list and forward_list ), vector 在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list forward_list统一的迭代器和引用更好。
使用 STL 的三个境界:能用,明理,能扩展 ,那么下面学习 vector ,我们也是按照这个方法去学习。

1.2vector的使用

        vector的学习可在官网查看文档。vector在实际中非常的重要,在实际中我们熟悉常用地接口就可以,下面列出了哪些接口是要重点掌握的。

1.2.1vector的定义

1.2.2vector iterator的使用

1.2.3vector空间增长问题

1.capacity 的代码在 vs g++ 下分别运行会发现, vs capacity 是按 1.5 倍增长的, g++ 是按 2 倍增长的
2.这个问题经常会考察,不要固化的认为, vector 增容都是 2 倍,具体增长多少是根据具体的需求定义 的。vs PJ 版本 STL g++ SGI 版本 STL
3.reserve 只负责开辟空间,如果确定知道需要用多少空间, reserve 可以缓解 vector 增容的代价缺陷问 题。
4.resize 在开空间的同时还会进行初始化,影响 size

1.2.4vector增删查改

 1.2.5vector迭代器失效问题(重点)

        迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了 封装 ,比如: vector 的迭代器就是原生态指针 T* 。因此 迭代器失效,实际就是迭代器底层对应指针所指向的 空间被销毁了,而使用一块已经被释放的空间 ,造成的后果是程序崩溃 ( 如果继续使用已经失效的迭代器, 程序可能会崩溃 )
对于 vector 可能会导致其迭代器失效的操作有:
1. 会引起其底层空间改变的操作,都有可能是迭代器失效 ,比如: resize reserve insert assign、 push_back等。
2. 指定位置元素的删除操作 - -erase
        erase删除 pos 位置元素后, pos 位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代 器不应该会失效,但是:如果pos 刚好是最后一个元素,删完之后 pos 刚好是 end 的位置,而 end 位置是 没有元素的,那么pos 就失效了。因此删除 vector 中任意位置上元素时, vs 就认为该位置迭代器失效了。
3.与vector 类似, string 在插入 + 扩容操作 +erase 之后,迭代器也会失效。
迭代器失效解决办法:
在使用前,对迭代器重新赋值即可。

2.vector的模拟实现源码

#include<iostream>
using namespace std;

namespace fish
{
    template<class T>
    class vector
    {
    public:
        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;
        }

        vector() {

        }
        vector(int n, const T& value = T()) {
            resize(n, value);
        }
        template<class InputIterator>
        vector(InputIterator first, InputIterator last) {
            if (first <= last) {
                reserve(last - first);
                for (InputIterator i = first;i < last;i++) {
                    push_back(*i);
                }
            }
        }
        vector(const vector<T>& v) {
            reserve(v.capacity());
            for (size_t i = 0;i < v.size();i++) {
                _start[i] = v._start[i];
                _finish++;
            }
        }
        vector<T>& operator= (vector<T> v) {
            vector<T> tmp(v);
            return tmp;
        }
        ~vector() {
            delete[] _start;
            _start = _finish = _endOfStorage = nullptr;
        }

        size_t size() const {
            return _finish - _start;
        }
        size_t capacity() const {
            return _endOfStorage - _start;
        }
        void reserve(size_t n) {
            size_t sz = size();
            if (n > sz) {
                T* tmp = new T[n];
                for (size_t i = 0;i < size();i++) {
                    tmp[i] = _start[i];
                }
                delete[] _start;
                _start = tmp;
                _finish = _start + sz;
                _endOfStorage = _start + n;
            }
        }
        void resize(size_t n, const T& value = T()) {
            reserve(n);
            for (size_t i = 0;i < n;i++) {
                _start[i] = value;
                _finish++;
            }
        }

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

        void push_back(const T& x) {
            if (size() == capacity())
                reserve(capacity() == 0 ? 4 : 2 * capacity());
            *_finish = x;
            _finish++;
        }
        void pop_back() {
            if (size() > 0) {
                _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) {
            size_t l = pos - _start;
            if (size() == capacity())
                reserve(capacity() == 0 ? 4 : 2 * capacity());
            pos = _start + l;
            if (pos <= _finish) {
                for (iterator i = _finish;i > pos;i--) {
                    *i = *(i - 1);
                }
                _finish++;
            }
            *pos = x;
            return pos;
        }
        iterator erase(iterator pos) {
            if (pos < _finish) {
                iterator i = pos;
                while (i + 1 < _finish) {
                    *i = *(i + 1);
                    i++;
                }
                _finish--;
            }
            return pos;
        }

    private:
        iterator _start = nullptr; 
        iterator _finish = nullptr; 
        iterator _endOfStorage = nullptr; 
    };
};
        源码实现了常用的增删查改操作和比较,模拟实现有助于进一步理解stl库中底层代码的实现。

网站公告

今日签到

点亮在社区的每一天
去签到