• 【C++Vector容器底层剖析】完成了Vector容器的一些常用函数接口


    朋友们好,这篇博客我们继续C++的初阶学习,最近我学习了C++中的STL库中的vector容器,对于常用容器,我们不仅要会使用其常用的函数接口,我们还有明白这些接口在其底层是如何实现的。所以特意整理出来一篇博客供我们学习和,如果文章中有理解不当的地方,还希望朋友们在评论区指出,我们相互学习,共同进步!

    一:基础框架

    template<class T>
    class vector
    {
    public:
    	typedef T* iterator;
    	typedef const T* const_iterator;
    		
    private:
    	iterator _start;//指向第一个元素
    	iterator _finish;//指向最后一个元素的下一个位置
    	iterator _endofstoage;//容量
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    二:迭代器实现

    const_iterator begin() const
    {
    	return _str;
    }
    const_iterator end() const
    {
    	return _str + _size;
    }
    
    iterator begin() 
    {
    	return _str;
    }
    
    iterator end() 
    {
    	return _str + _size;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    三:size,capacity,resize,reserve

    		size_t size() const
    		{
    			return _finish - _start;
    		}
    
    		size_t capacity() const
    		{
    			return _endofstoage - _start;
    		}
    
    		void reserve(size_t n)
    		{
    			size_t sz = size();
    			if (n > capacity())
    			{
    				T* tmp = new T[n];
    				//T* tmp = (T*)malloc(sizeof(T)*n);
    				if (_start)
    				{
    					//memcpy(tmp, _start, size()*sizeof(T));
    
    					for (size_t i = 0; i < size(); ++i)
    					{
    						tmp[i] = _start[i];
    					}
    
    					delete[] _start;
    				}
    
    				_start = tmp;
    			}
    
    			_finish = _start + sz;
    			_endofstoage = _start + n;
    		}
    
    		//void resize(size_t n, const T& val = T())
    		void resize(size_t n, T val = T())//T类型的匿名对象做缺省参数,调用T的默认构造函数
    		{
    			if (n > capacity())
    			{
    				reserve(n);
    			}
    
    			if (n > size())
    			{
    				while (_finish < _start + n)
    				{
    					*_finish = val;
    					++_finish;
    				}
    			}
    			else
    			{
    				_finish = _start + n;
    			}
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    注意点:在reservr函数中,在拷贝的时候,不可以简单的通过memcpy函数来浅拷贝,因为当T是涉及到深浅拷贝的类型时,使用memcpy会存在深浅拷贝释放内存空间的问题。

    四:insert,erase

    		iterator insert(iterator pos, const T& x)
    		{
    			// 检查参数
    			assert(pos >= _start && pos <= _finish);
    
    			// 扩容
    			// 扩容以后pos就失效了,需要更新一下
    			if (_finish == _endofstoage)
    			{
    				size_t n = pos - _start;
    
    				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
    				reserve(newCapacity);
    
    				pos = _start + n;
    			}
    
    			// 挪动数据
    			iterator end = _finish - 1;
    			while (end >= pos)
    			{
    				*(end + 1) = *end;
    				--end;
    			}
    
    			*pos = x;
    			++_finish;
    
    			return pos;
    		}
    
    
    		iterator erase(iterator pos)
    		{
    			assert(pos >= _start && pos < _finish);
    			iterator it = pos + 1;
    			while (it != _finish)
    			{
    				*(it - 1) = *it;
    				++it;
    			}
    
    			--_finish;
    
    			return pos;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    注意点:在insert函数中,如果需要扩容的话,注意扩容前后pos位置的更新,其实STL库中也进行了这样的更新,不更新的话位置就失效了 。

    五:pop_back,push_back

    		void push_back(const T& x)
    		{
    			/*if (_finish == _endofstoage)
    			{
    				size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
    				reserve(newcapacity);
    			}
    			*_finish = x;
    			_finish++;*/
    			insert(end(), x);
    		}
    
    		void pop_back()
    		{
    			erase(end() - 1);//复用
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    注意点:可以直接复用insert和erase函数。

    六:operator[]

    		T& operator[](size_t pos)
    		{
    			assert(pos < size());
    			return *(_start + pos);
    		}
    
    		const T& operator[](size_t pos) const
    		{
    			assert(pos < size());
    			return *(_start + pos);
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注意点:分别针对常对象和普通对象。

    七:构造函数,析构函数,赋值重载

    		vector()
    			:_start(nullptr)
    			, _finish(nullptr)
    			, _endofstoage(nullptr)
    		{}
    
    		//为什么要有这个
    		//是为了拷贝构造的现代写法时有一个可用的有参构造可以用
    		template <class InputIterator>
    		vector(InputIterator first, InputIterator last)
    			: _start(nullptr)
    			, _finish(nullptr)
    			, _endofstoage(nullptr)
    		{
    			while (first != last)
    			{
    				push_back(*first);
    				++first;
    			}
    		}
    
    		//n个val调用的构造函数
    		vector(size_t n, const T& val = T())//用一个匿名对象做缺省参数
    			: _start(nullptr)
    			, _finish(nullptr)
    			, _endofstoage(nullptr)
    		{
    			reserve(n);
    			for (size_t i = 0; i < n; ++i)
    			{
    				push_back(val);
    			}
    		}
    
    
    		vector(int n, const T& val = T())
    			: _start(nullptr)
    			, _finish(nullptr)
    			, _endofstoage(nullptr)
    		{
    			reserve(n);
    			for (int i = 0; i < n; ++i)
    			{
    				push_back(val);
    			}
    		}
    
    		void swap(vector<T>& v)
    		{
    			std::swap(_start, v._start);
    			std::swap(_finish, v._finish);
    			std::swap(_endofstoage, v._endofstoage);
    		}
    
    		
    		//vector(const vector& v);
    		vector(const vector<T>& v)
    			: _start(nullptr)
    			, _finish(nullptr)
    			, _endofstoage(nullptr)
    		{
    			vector<T> tmp(v.begin(), v.end());
    			swap(tmp);
    		}
    
    		//vector& operator=(vector v)
    		vector<T>& operator=(vector<T> v)
    		{
    			swap(v);
    			return *this;
    		}
    
    		// 资源管理
    		~vector()
    		{
    			if (_start)
    			{
    				delete[] _start;
    				_start = _finish = _endofstoage = nullptr;
    			}
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    注意点1: 赋值重载的形参列表利用传值传参,调用了拷贝构造完成了深拷贝,直接交换!
    注意点2:注意这种拷贝构造和赋值重载的现代写法(请人干活,窃取果实),但必须得有对应的有参构造!

  • 相关阅读:
    【nosql】redis之高可用(主从复制、哨兵、集群)搭建
    人保爱无忧易核版重疾险怎么样?好不好?
    一键云端,AList 整合多网盘,轻松管理文件多元共享!
    如何用 ESP-AT 进行多种场景下的 UDP 透传
    STM32——DHT11温湿度传感器
    判断两个链表是否相交
    信号发送与处理
    微信小程序日历插件对比
    EDA国产化之路,路在何方?
    C语言:位段、枚举、联合体
  • 原文地址:https://blog.csdn.net/qq_43727529/article/details/125871697