STL-Vector

本文介绍了C++STL中的Vector容器,包括其工作原理、动态扩容机制、内存分配策略,以及增删查改操作。同时讨论了迭代器失效的问题及其处理方法。
摘要由CSDN通过智能技术生成

我们将进入到C++STL 的学习。STL在各各C++的头文件中,以源代码的形式出现,不仅要会用,还要了解底层的实现。源码之前,了无秘密。

STL六大组件

image-20240323151905853

Container通过Allocator取得数据储存空间,Algorithm通过Iterator存取Container内容,Functor可以协助Algorithm完成不同的策略变化,Adapter可以修饰或者套接Functor。

序列式容器sequential containers

Vector

image-20240402214309732

大概的意思是vector是表示可变大小数组的序列容器,就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。

本质来说,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。

vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。

因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在
末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。

Vector的定义

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

Vector空间增长

size获取数据个数
capacity获取容量大小
empty判断是否为空
resize改变vector的size
reserve改变vector放入capacity

reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
resize在开空间的同时还会进行初始化,影响size。

capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。但是不要固化的认为,顺序表增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。

Vector增删查改

push_back尾插
pop_back尾删
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
operator[]像数组一样访问
find查找(算法模块 )
#include<iostream>
#include<vector>
using namespace std;

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	vector<int>::iterator pos = find(v.begin(), v.end(), 2);
	v.insert(pos, 4); //在2前面插入4
	for (auto x : v) cout << x << endl;
	vector<int>v1;
	v1.swap(v);
	for (auto x : v1) cout << x << endl;
	for (auto x : v) cout << x << endl; //啥也没有了
}

Vector模拟实现

image-20240404143515689

具体代码:vector模拟实现具体代码

迭代器部分

	class vector
	{	
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}
	};

构造析构

class vector
{
    vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_end_of_storage(nullptr)
		{}
		
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			while (first != last)
			{
				push_back(*first++);
			}
		}

		vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			//方法1:
			/*_start = new T[v.capacity()];
			_finish = _start + v.size();
			_end_of_storage = _start + v.capacity();
			memcpy(_start, v._start, v.size() * sizeof(T));*/ //会出现浅拷贝的问题

			//方法2
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);

		}
	
		~vector()
		{
			if (_start != nullptr) _start = nullptr;
			_start = _finish = _end_of_storage = nullptr;
		}
	
};

增删改查赋值

class vector
{
  size_t size() const
		{
			return _finish - _start;
		}
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}

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

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

		void reserve(size_t n)
		{
			if (n > capacity())
			{
			size_t old_size = size();
			T* tmp = new T[n];
			if (_start)
			{
			//memcpy拷贝原来空间的数据,如果里面是一个string类型,在析构的时候就会崩了
			//memcpy(tmp, _start, sizeof(T) * size()); 
				for (size_t i = 0; i < old_size; i++)
				{
					tmp[i] = _start[i]; //如果T是自定义类型,会去调用它的赋值拷贝
				}
				delete[] _start;
			}
				_start = tmp;
				_finish = _start + old_size;
				_end_of_storage = _start + n;
			}
	
		}

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

				}
			}
		}
		void push_back(const T& x)
		{
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = x;
			++_finish;
		}

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

		void pop_back()
		{
			assert(_finish > _start);
			--_finish;
		}
		// pos前面插入一个x
		iterator insert(iterator pos, const T& x) 
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			//扩容会导致pos失效
			if (_finish == _end_of_storage)
			{
				size_t len = pos - _start; //记录pos位置
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len; //还原pos位置
			}
			//挪动数据
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end; 
				end--;
				
			}
			*pos = x;
			++_finish;
			return pos;
		}

		//删除pos位置的值
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			iterator begin = pos + 1;
			while (begin < _finish)
			{
				*(begin - 1) = *(begin);
				begin++;
			}
			_finish--;
			return pos;
		}
		
		
		void swap(vector<T>& tmp)
		{
			std::swap(_start, tmp._start);
			std::swap(_finish, tmp._finish);
			std::swap(_end_of_storage, tmp._end_of_storage);
		}
  
};

迭代器失效

只要使用迭代器访问容器,都可能涉及迭代器失效,vector迭代器的失效主要发生在insert/erase。

//发生了扩容后,迭代器失效,程序崩了
void test_pop_insert_erase()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::iterator x = std::find(v.begin(), v.end(), 3);
	if (x != v.end()) 
		//迭代器失效:发生扩容后,x指向的原空间被释放掉,那么x是一个野指针
		v.insert(x, 10); //这个时候x已经失效了,如果想继续用得用一个值接收insert的返回值
	vector<int>:: iterator it = v.begin();
	while (it != v.end())
		std::cout << *it++ << std::endl;
-------------------------------------------------------------------------------------
	std::cout << "..................." << std::endl;
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	vector<int>::iterator it2 = v1.begin();
	while (it2 != v1.end())
	{
		if (*it2 == 1)
		{
			it2 = v1.erase(it2); //删除之后要更新it2到下一个位置
		}
		it2++;
	}
	for (auto x : v1) std::cout << x << std::endl;
}

  • 43
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值