C++List容器常用函数接口刨析

目录
  • 一、基本结构
  • 二、list的迭代器的构造
  • 三、迭代器的实现
  • 四、insert,erase
  • 五、push_back,push_front,pop_back,pop_front
  • 六、构造函数与赋值重载
  • 七、析构与清空

一、基本结构

由源代码可知,list容器是有一个带头双向循环链表实现,所以我们模拟实现也需要实现一个带头双向循环链表的数据结构。

template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _data;
		list_node(const T& val = T())//用一个匿名对象来做缺省参数
			:_next(nullptr)
			, _prev(nullptr)
			, _data(val)
		{}
	};
template<class T>
	class list
	{
	public:
		typedef list_node<T> Node;
	private:
		Node* _head;
	};

二、list的迭代器的构造

list的迭代器与vector的迭代器不一样,list的迭代器是一个自定义类型的对象,成员变量包含一个指向节点的指针。

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* node)
			:_node(node)
		{}
		// 析构函数  -- 节点不属于迭代器,不需要迭代器释放
		// 拷贝构造和赋值重载 -- 默认生成的浅拷贝就可以
		// *it
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			//return &(operator*());
			return &_node->_data;
		}
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		self operator++(int)
		{
			self tmp(*this);//拷贝构造
			_node = _node->_next;
			return tmp;//因为tmp出了作用域就不在了,所以不可以引用返回
		}
		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

️️️即用一个自定义类型封装,通过运算符的重载使迭代器实现像指针一样的操作行为。

三、迭代器的实现

	template<class T>
	class list
	{
		typedef list_node<T> Node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;
		//仅仅是为了改名,如果不是为了改名,不用写。
		__list_iterator<T, const T&, const T*> begin() const
		{
			// list_node<int>*
			return __list_iterator<T, const T&, const T*>(_head->_next);
			//构造一个匿名对象
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}
		iterator begin()
		{
			return iterator(_head->_next);//构造一个匿名对象来返回
			//return _head->_next;//也可以,因为单参数构造函数支持隐式类型转换。
			//iterator it = _head->_next   隐式调用
		}
		iterator end()
		{
			return iterator(_head);
		}
	}

四、insert,erase

		// 插入在pos位置之前
		iterator insert(iterator pos, const T& x)//pos是一个迭代器对象
		{
			Node* newNode = new Node(x);
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			// prev  newnode  cur
			prev->_next = newNode;
			newNode->_prev = prev;
			newNode->_next = cur;
			cur->_prev = newNode;
			return iterator(newNode);
		}
		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			// prev  next
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return iterator(next);
		}

五、push_back,push_front,pop_back,pop_front

void push_back(const T& x)
		{
			//Node* tail = _head->_prev;
			//Node* newnode = new Node(x);
			 _head    tail  newnode
			//tail->_next = newnode;
			//newnode->_prev = tail;
			//newnode->_next = _head;
			//_head->_prev = newnode;
			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		void pop_back()
		{
			erase(--end());
			//这里不可以用end()-1吧,因为尾部迭代器在尾删后是需要变得
		}
		void pop_front()
		{
			erase(begin());
		}

️这里均复用了insert和erase函数。

六、构造函数与赋值重载

		list()//带头双向循环链表,初始化要先把头弄出来
		{
			_head = new Node();
			//自定义类型去调用对应类的构造函数,带不带这个括号都可
			_head->_next = _head;
			_head->_prev = _head;
		}
		// lt2(lt1)————传统写法
		/*list(const list<T>& lt)
		{
		_head = new Node();
		_head->_next = _head;
		_head->_prev = _head;
		for (auto e : lt)
		{
		push_back(e);//push_back中复用insert,insert中完成深拷贝
		}
		}*/
		void empty_init()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
		}
		//如果我们写现代写法,那么必须提供相应的带参构造
		template <class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);//push_back中调用insert时会完成相应深拷贝
				++first;
			}
		}
		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);//交换头节点
		}
		// lt2(lt1) -- 现代写法
		list(const list<T>& lt)
		{
			empty_init();//总不能把一个野指针换给别人呀!
			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}
		// lt2 = lt1
		list<T>& operator=(list<T> lt)
		//list<T> lt = lt1,传值传参这一步就调用了拷贝构造完成深拷贝
		{
			swap(lt);
			return *this;
		}

️️️注意现代写法的方法

七、析构与清空

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);//用返回值更新,防止迭代器失效
			}
		}

到此这篇关于C++List容器常用函数接口刨析的文章就介绍到这了,更多相关C++List容器 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++顺序容器(vector、deque、list)的使用详解

    目录 一:STL(Standard Template Library),即标准模板库,是一个高效的C++程序库 二:STL组件 三:容器 四:类型成员 五:迭代器 六:顺序容器 七:顺序容器--向量(vector) 八:顺序容器--双端队列--deque 九:顺序容器 --列表--list 一:STL(Standard Template Library),即标准模板库,是一个高效的C++程序库 1.从实现层次看,整个STL是以一种类型参数化(type parameterized)的方式实现的,基

  • c++ STL容器总结之:vertor与list的应用

    STL提供六大组件,彼此可以组合套用 1.容器(containers):各种数据结构,如vertor,list,deque,set,map.从实现的角度来看,STL容器是一种class template 2.算法(algorithms):各种算法如sort,search,copy,earse.STL算法是一种 function template. 3.迭代器(iterators):扮演容器与算法之间的胶合剂,是所谓的"泛型指针".所有STL容器都有自己的专属的迭代器. 4.仿函数(fu

  • c++容器list、vector、map、set区别与用法详解

    c++容器list.vector.map.set区别 list 封装链表,以链表形式实现,不支持[]运算符. 对随机访问的速度很慢(需要遍历整个链表),插入数据很快(不需要拷贝和移动数据,只需改变指针的指向). 新添加的元素,list可以任意加入. vector 封装数组,使用连续内存存储,支持[]运算符. 对随机访问的速度很快,对头插元素速度很慢,尾插元素速度很快 新添加的元素,vector有一套算法. map 采用平衡检索二叉树:红黑树 存储结构为键值对<key,value> set 采用

  • C++语言 STL容器list总结

    在使用std::list<>链表时,难免会对数据进行添加删除操作.而遍历链表则有两种方式:通过索引访问,象数组一样处理:通过std::list<>::iterator链表遍历器进行访问 STL 中的list 就是一 双向链表,可高效地进行插入删除元素. list不支持随机访问.所以没有 at(pos)和operator[]. list 对象list1, list2 分别有元素list1(1,2,3),list2(4,5,6) .list< int>::iterator

  • C++List容器常用函数接口刨析

    目录 一.基本结构 二.list的迭代器的构造 三.迭代器的实现 四.insert,erase 五.push_back,push_front,pop_back,pop_front 六.构造函数与赋值重载 七.析构与清空 一.基本结构 由源代码可知,list容器是有一个带头双向循环链表实现,所以我们模拟实现也需要实现一个带头双向循环链表的数据结构. template<class T> struct list_node { list_node<T>* _next; list_node&

  • C++Vector容器常用函数接口详解

    目录 一.基础框架 二.迭代器实现 三.size capacity resize reserve 四.insert,erase 五.pop_back,push_back 六.operator[] 七.构造函数 析构函数 赋值重载 一.基础框架 template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; private: iterator _start;//指向第一个

  • PHP封装curl的调用接口及常用函数详解

    如下所示: <?php /** * @desc 封装curl的调用接口,post的请求方式 */ function doCurlPostRequest($url, $requestString, $timeout = 5) { if($url == "" || $requestString == "" || $timeout <= 0){ return false; } $con = curl_init((string)$url); curl_setop

  • Java 逻辑结构与方法函数详解刨析

    ⭐前言⭐ 本文主要介绍JavaSE的逻辑结构和方法. 对一门编程语言逻辑结构和方法的理解是站在C语言之上的,建议配套C语言版本的分析一起食用 链接直达:

  • C++深入刨析优先级队列priority_queue的使用

    目录 一.priority_queue的介绍 二.priority_queue的使用 三.priority_queue的模拟实现 四.容器适配器 4.1.什么是适配器 4.2.适配模式 4.3.STL标准库中stack和queue的底层结构 一.priority_queue的介绍 priority_queue官方文档介绍 翻译: 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中

  • Java项目工程代码深度刨析总结

    目录 一.背景 二.衡量代码好环的原则 2.1 评判代码指标 2.2 指导理论 三.代码实现技巧 3.1 抽像能力 3.2 组合/聚合复用原则 四.总结 一.背景   最近我们团队有幸接了两个0到1的项目,一期项目很紧急,团队成员也是加班加点,从开始编码到完成仅用了一星期多一点点,期间还不断反复斟酌代码如何抽象代码,如何写得更优雅,一遍又一遍的调整,我也是一次又次的阅读每个团队成员的代码,虽然还有些不如意,但整体来说还算是满意,参与项目的成员经过不断琢磨,对一些功能不断抽像,团队进步也是非常明显

  • Java集合框架之List ArrayList LinkedList使用详解刨析

    目录 1. List 1.1 List 的常见方法 1.2 代码示例 2. ArrayList 2.1 介绍 2.2 ArrayList 的构造方法 2.3 ArrayList 底层数组的大小 3. LinkedList 3.1 介绍 3.2 LinkedList 的构造方法 4. 练习题 5. 扑克牌小游戏 1. List 1.1 List 的常见方法 方法 描述 boolean add(E e) 尾插 e void add(int index, E element) 将 e 插入到 inde

  • Java 关于时间复杂度和空间复杂度的深度刨析

    目录 1.算法效率 2.时间复杂度 2.1时间复杂度的概念 2.2大O的渐进表示法 2.3常见时间复杂度计算 2.3.1常用的时间复杂度量级 2.3.2常见示例举例 2.3.2示例答案及分析 3.空间复杂度 1.算法效率 算法效率分析分为两种:第一种是时间效率,第二种是空间效率.时间效率被称为时间复杂度,而空间效率被称作空间复杂度. 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间 如今我们更关注的是时间复杂度,而对空间复杂度已不再关注. 2.时间复杂度 2

  • C++深入刨析muduo中的抽象类Poller

    目录 Poller是抽象类,Eventloop通过抽象类Poller,引用不同的派生类对象(PollPoller或EpollPoller),调用同名覆盖方法,就可以很方便地去扩展不同的I/O复用 Poller.h源码 #include <map> #include <vector> #include "muduo/base/Timestamp.h" #include "muduo/net/EventLoop.h" namespace mudu

  • Java面向对象特性深入刨析封装

    目录 1.认识封装 2.控制访问权限-访问修饰符 3.理解封装必须要知道-包 3.1理解包的概念 3.2 导入包中的类 3.3 自定义包 3.4 包的访问权限控制 3.5 java中常见的包 前面已经提过了 Java是一门面向对象(oop)的进行编程的语言, 面向对象的编程,有很多的好处,比如更容易开拓思维,分工合作,提高开发效率, 最主要的是 可重用性高,也就是下面将要提到的这三个核心特性(封装,继承,多态). 可扩展性,易于管理. 1.认识封装 简单的一句话就是套壳屏蔽细节. 比如说一部手机

随机推荐