STL-vector以及list使用和详细剖析实现

C语言中有数组,有各种内置类型的数组,例如int、double、char。但是C++是一种面向对象的思想,编写C++代码时有许多的类(自定义类型)。需要像内置类型一样把他们像数组一样,C语言是不支持的。所以c++引入了容器的概念。

vector

vector介绍

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

vector的使用

构造函数

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

增删查改接口

//尾插
void push_back (const value_type& val);
//尾删
void pop_back();

//插入
iterator insert (iterator position, const value_type& val);//在position插入val
void insert (iterator position, size_type n, const value_type& val);//在position位置插入n个val值
	template <class InputIterator>    
void insert (iterator position, InputIterator first, InputIterator last);//使用迭代器区间在position位置插入若干个值

iterator erase (iterator position);//删除position位置的值

//和x进行值交换
void swap (vector& x);

void reserve (size_type n);								  //为vector预开辟n个空间,若是n小于capacity则什么都不做
void resize (size_type n, value_type val = value_type()); //为vector预开辟空间并初始化。

迭代器的使用

begin();//获取容器的第一个位置
end();//获取容器的最后一个元素的下一个位置

rbegin();//获取容器的最后一个元素的下一个位置
rend();//获取容器的第一个位置

//正向迭代器的使用
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int>::iterator it = v1.begin();
while (it != v1.end()) {
	cout << *it << " ";
	it++;
}
	cout << endl;
}
//输出1 2 3 4

//反向迭代器的使用
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int>::reverse_iterator rit = v1.rbegin();
while (rit != v1.rend()) {
	cout << *rit << " ";
	rit++;
}
	cout << endl;
}
//输出4 3 2 1

vector模拟实现

#pragma once
#include <iostream>
#include <stdlib.h>
#include <assert.h>
#include <algorithm>
using namespace std;
namespace JRG {
	//反向迭代器
	template<class Iterator, class Ref, class Ptr>
	struct reverseIterator
	{
		typedef reverseIterator<Iterator, Ref, Ptr> self;
		Iterator it;
		reverseIterator(Iterator val)
			:it(val)
		{}
		Ref operator*() {
			Iterator tem = it;
			tem--;
			return *tem;
		}
		Ptr operator->() {
			return it;
		}
		self& operator++() {
			--it;
			return *this;
		}
		self operator++(int) {
			self tem(it);
			--it;
			return tem;
		}
		self& operator--() {
			++it;
			return *this;
		}
		bool operator!=(const self& ls) {
			return it != ls.it;
		}
		bool operator==(const self& ls) {
			return it == ls.it;
		}
	};
	template<class T>
	class vector {
	public:
		//正向迭代器
		typedef T* iterator;
		typedef const T* const_iterator;
		
		//使用正向迭代器定义反向迭代器
		typedef reverseIterator<iterator, T&, T*> reverse_iterator;
		typedef reverseIterator<iterator, const T&, const T*> const_reverse_iterator;
		
		//构造函数
		vector() 
			:_start(nullptr)
			,_finish(nullptr)
			,_end_of_storage(nullptr)
		{}
		
		vector(int n, const T& value = T()) 
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			resize(n,value);
		}
		//使用迭代器初始化
		template<class Inputlterator>
		vector(Inputlterator first, Inputlterator last) 
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			reserve(last - first);
			Inputlterator next = first;
			while (next != last) {
				push_back(*next);
				next++;
			}
		}
		void swap(vector<T>& v) {
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}
		//拷贝构造
		vector(const vector<T>& v) 
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			/*reserve(v.capacity());
			for (size_t i = 0; i < v.size(); ++i) {
				push_back(v._start[i]);
			}*/
			vector<T> tem(v.begin(), v.end());
			swap(tem);
		}
		vector<T>& operator=(vector<T> v) 
		{
			/*reserve(v.capacity());
			for (size_t i = 0; i < v.size(); ++i) {
				push_back(v._start[i]);
			}*/
			swap(v);
			return *this;
		}

		~vector() {
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
		reverse_iterator rbegin() {
			return reverse_iterator(end());
		}
		reverse_iterator rend() {
			return reverse_iterator(begin());
		}
		iterator begin() {
			return _start;
		}
		iterator end() {
			return _finish;
		}
		const_iterator begin() const{
			return _start;
		}
		const_iterator end() const{
			return _finish;
		}
		size_t capacity() const{
			return _end_of_storage - _start;
		}
		size_t size() const{
			return _finish - _start;
		}
		void resize(size_t n, T val = T()) {
			if (n < size()) {
				_finish = _start + n;
			}
			else {
				if (n > capacity()) {
					reserve(n);
				}
				while (_finish != _start + n) {
					*_finish = val;
					_finish++;
				}

			}
		}
		void reserve(size_t n) {
			if (n > capacity()) {
				size_t sz = size();
				T* tem = new T[n];
				if (_start) {
					//memcpy(tem, _start, sizeof(T)*size());  对于自定义类型是浅拷贝
					for (size_t i = 0; i < size(); ++i) {
						tem[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tem;
				_finish = tem + sz;
				_end_of_storage = tem + n;

			}
		}
		void push_back(const T& val) {
			if (size() <= capacity()) {
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = val;
			_finish++;
		}
		void pop_back() {
			assert(!empty());
			_finish--;
		}
		iterator insert(iterator pos, const T& val) {
			assert(pos >= _start && pos <= _finish);
			if (size() <= capacity()) {
				size_t sz = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				//扩容之后pos指向的位置不存在,因为扩容是异地扩容,不是在原来的空间上扩容,所以需要给pos重新赋值,以免迭代器失效
				pos = _start + sz;
			}
			iterator tem = _finish-1;
			while (tem >= pos) {
				*(tem+1) = *tem;
				tem--;
			}
			*pos = val;
			_finish++;
			return pos;
		}
		void erase(iterator pos) {
			assert(!empty());
			assert(pos >= _start && pos < _finish);
			while (pos<_finish-1) {
				*pos = *(pos + 1);
				pos++;
			}
			_finish--;
		}
		T& operator[](size_t pos) {
			assert(pos >= 0 && pos < size());
			return *(_start + pos);
		}
		const T& operator[](size_t pos) const{
			assert(pos >= 0 && pos < size());
			return *(_start + pos);
		}

		bool empty() {
			return _finish == _start;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};
}

list

list介绍

在C语言中学习的list链表其实就是模仿STL库中的list链表,不过STL中的链表是用类封装起来了。可以看看C语言实现的链表:链接: link

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

list的使用

构造函数

list (size_type n, const value_type& val = value_type())	//构造n个值为val的链表
list()														//空构造
list (const list& x)										//拷贝构造
list (InputIterator first, InputIterator last)				//使用迭代器构造

常用函数

void push_front (const value_type& val);						//链表的头插
void pop_front();												//链表的头删
void push_back (const value_type& val);							//链表的尾插
void pop_back();												//链表的尾删
iterator insert (iterator position, const value_type& val);		//在position位置插入
iterator erase (iterator position);								//删除position位置元素
void clear();													//删除链表所有值
void swap (list& x);											//交换链表值

list模拟实现

namespace JRG {
	//一个node节点
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _val;
		list_node(const T& x = T())
			:_next(nullptr)
			, _prev(nullptr) 
			, _val(x)
		{}
	};
	//反向迭代器
	template<class Iterator, class Ref, class Ptr>
	struct reverseIterator
	{
		typedef reverseIterator<Iterator, Ref, Ptr> self;
		Iterator it;
		reverseIterator(Iterator val)
			:it(val)
		{}
		Ref operator*() {
			Iterator tem = it;
			tem--;
			return *tem;
		}
		Iterator operator->() {
			Iterator tem = it;
			tem--;
			return tem;
		}
		self& operator++() {
			--it;
			return *this;
		}
		self operator++(int) {
			self tem(it);
			--it;
			return tem;
		}
		self& operator--() {
			++it;
			return *this;
		}
		bool operator!=(const self& ls) {
			return it != ls.it;
		}
		bool operator==(const self& ls) {
			return it == ls.it;
		}
	};
	//迭代器
	template<class T,class Ref,class Ptr>
	struct list_iterator {
		typedef list_node<T> node;
		typedef list_iterator<T,Ref,Ptr> self;
		node* _node;
		list_iterator(node* n) 
			:_node(n)
		{}
		Ref operator*() {
			return _node->_val;
		}
		Ptr operator->() {
			return &_node->_val;
		}
		self& operator++() {
			_node = _node->_next;
			return *this;
		}
		self operator++(int) {
			self tem(_node);
			_node = _node->_next;
			return tem;
		}
		self& operator--() {
			_node = _node->_prev;
			return *this;
		}
		self operator--(int) {
			self tem(_node);
			_node = _node->_prev;
			return tem;
		}
		bool operator!=(const self& ls) {
			return _node != ls._node;
		}
		bool operator==(const self& ls) {
			return _node == ls._node;
		}
	};
	template<class T>
	class list
	{
		typedef list_node<T> node;
	public:
		//迭代器
		typedef list_iterator<T, T&,T*> iterator;
		//const迭代器
		typedef list_iterator<T, const T&,const T*> const_iterator;

		//使用正向迭代器构建反向迭代器
		typedef reverseIterator<iterator, T&, T*> reverse_iterator;
		typedef reverseIterator<iterator, const T&, const T*> const_reverse_iterator;

		reverse_iterator rbegin() {
			return reverse_iterator(end());
		}
		reverse_iterator rend() {
			return reverse_iterator(begin());
		}
		iterator begin() {
			return iterator(head->_next);
		}
		iterator end() {
			return iterator(head);
		}
		const_iterator begin()const {
			return const_iterator(head->_next);
		}
		const_iterator end()const {
			return const_iterator(head);
		}
		void EmptyInit() {
			head = new node;
			head->_next = head;
			head->_prev = head;
		}
		list() 
		{
			/*head = new node;
			head->_next = head;
			head->_prev = head;*/
			EmptyInit();
		}
		template <class Iterator>
		list(Iterator first, Iterator last) {
			EmptyInit();
			while (first != last) {
				push_back(*first);
				first++;
			}
		}
		void swap(list<T>& ls) {
			std::swap(head, ls.head);
		}
		list(const list<T>& ls) {
			list<T> tem(ls.begin(), ls.end());
			swap(tem);
		}
		list<T>& operator=(list<T> ls) {
			swap(ls);
			return *this;
		}
		//删除除头节点以外的所有节点
		void clear() {
			/*node* next = head->_next;*/
			iterator it = begin();
			while (it != end()) {
				/*node* tem = next;
				next = next->_next;
				delete tem;*/
				erase(it++);
			}
		}
		~list() {
			clear();
			delete head;
			head = nullptr;
		}
		//尾插
		void push_back(const T& x) {
			/*node* newnode = new node(x);
			node* prev = head->_prev;
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = head;
			head->_prev = newnode;*/
			insert(end(), x);
		}
		//头插
		void push_front(const T& x) {
			insert(begin(), x);
		}
		//尾删
		void pop_back() {
			erase(--end());
		}
		//头删
		void pop_front() {
			erase(begin());
		}
		//pos节点前插入节点
		void insert(iterator pos, const T& x) {
			node* prev = pos._node->_prev;
			node* next = pos._node;
			
			node* newnode = new node(x);

			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = next;
			next->_prev = newnode;

		}
		//删除pos节点
		void erase(iterator pos) {
			assert(pos != end());
			node* prev = pos._node->_prev;
			node* next = pos._node->_next;

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;
		}
	private:
		node* head;
	};
}

list和vector的区别

区别vectorlist
底层结构动态顺序表,一段连续的空间带头节点的双向循环链表
随机访问支持随机访问,访问某个元素效率高不支持随机访问,
插入和删除随机插入和删除效率低下,扩容效率低随机插入和删除效率高,扩容代价低
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器原生态指针对原生态指针进行封装
迭代器失效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使用场景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随机访问

这就是vector和list的详解,希望对您有所帮助。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STL是指标准模板库(Standard Template Library),它是C++语言的一部分,提供了一系列的模板类和函数,用于支持通用的数据结构和算法。STL的目标是提供高效、可重用和可扩展的组件,以便开发人员能够更轻松地编写高质量的代码。STL包含了许多常见的数据结构,如vectorlist、set、map等,以及各种算法,比如排序、查找、遍历等。通过使用STL,开发人员可以更加高效地处理各种数据结构和算法的问题,提高代码的开发效率和质量。 在STL中,我们可以使用各种容器来存储和管理数据。例如,我们可以使用std::map来创建一个键值对的映射,其中每个键都有一个与之相关联的值。下面是一个示例代码,展示了如何创建和使用一个std::map对象: std::map<std::string, int> disMap() { std::map<std::string, int> tempMap{ {"C语言教程",10},{"STL教程",20} }; return tempMap; } std::map<std::string, int> newMap(disMap()); 在这个示例中,disMap()函数创建了一个临时的std::map对象,并初始化了其中的一些键值对。然后,使用移动构造函数将这个临时对象移动到了一个新的std::map对象newMap中。最终,我们可以通过newMap对象来访问和操作这些键值对。 综上所述,STLC++中的标准模板库,提供了一系列的模板类和函数,用于支持通用的数据结构和算法。STL使用可以提高代码的开发效率和质量,并且通过各种容器和算法,可以方便地处理各种数据结构和算法的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ STL详解超全总结(快速入门STL)](https://blog.csdn.net/qq_50285142/article/details/114026148)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【C++实验】阅读STL源码并分析](https://blog.csdn.net/qq_35760825/article/details/125311509)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值