source Insight看STL源码——List

2023-11-02

上一章学vector,不夸张的说大半的时间都在往回找代码上了,不断被typedef不断回看,哪怕可以跳转也找得很累,再一次验证了充分利用工具学习得必要性,接下来要剖析STL源码同时力扣的动态规划现在整理好同时其他分类也要跟上。还有重要的项目(数据结构、算法、项目、源码)

せの☀


list不仅是一个双向链表,而且还是一个环状双向链表。另外,还有一个重要性质,插入操作和接合操作都不会造成原有的list迭代器失效,这在vector是不成立的。因为vector的插入操作可能造成记忆体重新配置,导致原有的迭代器全部失效。甚至list的元素删除操作(erase),也只有“指向被删除元素”的那个迭代器失效,其他迭代器不受任何影响。

一、list中的数据元素

一个list里需要什么才能够控制双向链表,
list类继承自List_val,List_val继承自List_nod,
在这里插入图片描述

发现整个list_nod类只有一个数据成员, -Node就是一个节点,节点的设计在代码中显示就是两个指针_Nodeptr类型和一个数据_Ty类型,分别再往回两行看到typedef可看到,_Nodeptr类型指出去还是指向_Node类型,完美,这可不就是双向链表吗

template<class _Ty,
	class _Alloc>
	class _List_nod
		: public _Container_base
	{	// base class for _List_val to hold storage
public:
	typedef typename _Alloc::template rebind<_Ty>::other _Alty;
	typedef typename _Alty::size_type size_type;

	struct _Node;
	typedef _Node *_Nodeptr;	// _Node allocator must have ordinary pointers
	typedef _Nodeptr& _Nodepref;

	struct _Node
		{	// list node
		_Nodeptr _Next;	// successor node, or first element if head
		_Nodeptr _Prev;	// predecessor node, or last element if head
		_Ty _Myval;	// the stored value, unused if head

	private:
		_Node& operator=(const _Node&);
		};

二、关于iterator

在list中可见iterator是一个类,其实我们也可以想到,因为iterator是一个指针,我们可以通过++或者–得到下一个节点的地址,但是list中一个节点(包括其他结构)都不是只要简单移动指针就行,是需要进入节点内部去看next或者prev的位置的,所以一定是需要一个类来完成这个操作
在这里插入图片描述
看看这个类:
首先因为iterator要模拟指针,所以肯定有大量的操作符存在(篇幅有限就不完全截图)
在这里插入图片描述
来有选择地看一下++这个运算符,分为前置++和后置++,代码中可见一个有参数(后置),一个没有参数(前置)

//typedef _List_const_iterator<_Mylist> _Mybase; 就把_Mybase看作是iterator自己的iterator    吗?(对
_Myiter& operator++()
		{	// preincrement
		++(*(_Mybase *)this);
		return (*this);
		}

	_Myiter operator++(int)//i++
		{	// postincrement
		_Myiter _Tmp = *this;//记录原值
		++*this;//进行操作
		return (_Tmp);//返回原来的值
		}

另外注意一下前++和后++的返回类型,C++不允许后++两次,所以为了阻止两次++,就不返回reference了

另外有五个typedef一定要写(所有的容器都
在这里插入图片描述

三、list的方法

明天再写详细的iterator部分及这部分代码整理
为了有整体概念,还是简介2.9版本:
代码转https://blog.csdn.net/JXH_123/article/details/31448847

 
template <class T>
struct __list_node
{
	typedef void* void_pointer;
	void_pointer next;
	void_pointer prev;
	T data;
};
 
// 至于为什么不使用默认参数, 这个是因为有一些编译器不能提供推导能力,
// 而作者又不想维护两份代码, 故不使用默认参数
template<class T, class Ref, class Ptr>
struct __list_iterator
{
	typedef __list_iterator<T, T&, T*>             iterator;   // STL标准强制要这个三个传参
	typedef __list_iterator<T, Ref, Ptr>           self;
 
	typedef bidirectional_iterator_tag iterator_category;
	typedef T value_type;
	typedef Ptr pointer;
	typedef Ref reference;
	typedef __list_node<T>* link_type;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
 
	link_type node;   //迭代器内部当然要有一个普通指针,指向list的节点
 
	__list_iterator(link_type x) : node(x) {}
	__list_iterator() {}
	__list_iterator(const iterator& x) : node(x.node) {}
 
	// 在STL算法中需要迭代器提供支持
	bool operator==(const self& x) const { return node == x.node; }
	bool operator!=(const self& x) const { return node != x.node; }
 
	// 以下对迭代器取值(dereference),取的是节点的数据值
	reference operator*() const { return (*node).data; }
 
	// 以下是迭代器的成员存取运算子的标准做法
	pointer operator->() const { return &(operator*()); }
 
	// 前缀自加,对迭代器累加1,就是前进一个节点
	self& operator++()
	{
		node = (link_type)((*node).next);
		return *this;
	}
 
	// 后缀自加, 需要先产生自身的一个副本, 然会再对自身操作, 最后返回副本
	self operator++(int)
	{
		self tmp = *this;
		++*this;
		return tmp;
	}
 
	// 前缀自减
	self& operator--()
	{
		node = (link_type)((*node).prev);
		return *this;
	}
 
	self operator--(int)
	{
		self tmp = *this;
		--*this;
		return tmp;
	}
};
 

// list不仅是个双向链表, 而且还是一个环状双向链表

//       end()              头结点             begin()
//         ↓                  ↓                  ↓
//      --------           --------           --------           --------
// ---->| next |---------->| next |---------->| next |---------->| next |------
// |    --------           --------           --------           --------     |
// |  --| prev |<----------| prev |<----------| prev |<----------| prev |<--| |
// |  | --------           --------           --------           --------   | |
// |  | | data |           | data |           | data |           | data |   | |
// |  | --------           --------           --------           --------   | |
// |  |                                                                     | |
// |  | --------           --------           --------           --------   | |
// ---|-| next |<----------| next |<----------| next |<----------| next |<--|--
//    | --------           --------           --------           --------   |
//    ->| prev |---------->| prev |---------->| prev |---------->| prev |----
//      --------           --------           --------           --------
//      | data |           | data |           | data |           | data |
//      --------           --------           --------           --------

 
// 默认allocator为alloc, 其具体使用版本请参照<stl_alloc.h>
template <class T, class Alloc = alloc>
class list
{
protected:
	typedef void* void_pointer;
	typedef __list_node<T> list_node;
 
	// 专属之空间配置器,每次配置一个节点大小
	typedef simple_alloc<list_node, Alloc> list_node_allocator;
 
public:
	typedef T value_type;
	typedef value_type* pointer;
	typedef value_type& reference;
	typedef list_node* link_type;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
 
	typedef __list_iterator<T, T&, T*>             iterator;
 
protected:
	link_type node ;     // 只要一个指针,便可表示整个环状双向链表
	// 分配一个新结点, 注意这里并不进行构造,
	// 构造交给全局的construct, 见<stl_stl_uninitialized.h>
	link_type get_node() { return list_node_allocator::allocate(); }
 
	// 释放指定结点, 不进行析构, 析构交给全局的destroy
	void put_node(link_type p) { list_node_allocator::deallocate(p); }
 
	// 产生(配置并构造)一个节点, 首先分配内存, 然后进行构造
	// 注: commit or rollback
	link_type create_node(const T& x)
	{
		link_type p = get_node();
		construct(&p->data, x);
		return p;
	}
 
	// 析构结点元素, 并释放内存
	void destroy_node(link_type p)
	{
		destroy(&p->data);
		put_node(p);
	}
 
protected:
	// 用于空链表的建立
	void empty_initialize()
	{
		node = get_node();   // 配置一个节点空间,令node指向它
		node->next = node;   // 令node头尾都指向自己,不设元素值
		node->prev = node;
	}
 
  // 创建值为value共n个结点的链表
  // 注: commit or rollback
	void fill_initialize(size_type n, const T& value)
	{
		empty_initialize();
		__STL_TRY
		{
			// 此处插入操作时间复杂度O(1)
			insert(begin(), n, value);
		}
		__STL_UNWIND(clear(); put_node(node));
	}
    
 
public:
	list() { empty_initialize(); }
 
	iterator begin() { return (link_type)((*node).next); }
 
	// 链表成环, 当指所以头节点也就是end
	iterator end() { return node; }
 
	// 头结点指向自身说明链表中无元素
	bool empty() const { return node->next == node; }
 
	// 使用全局函数distance()进行计算, 时间复杂度O(n)
	size_type size() const
	{
		size_type result = 0;
		distance(begin(), end(), result);
		return result;
	}
 
	size_type max_size() const { return size_type(-1); }
	reference front() { return *begin(); }
	reference back() { return *(--end()); }
 
	
	// 在指定位置插入元素
	
	//       insert(iterator position, const T& x)
	//                       ↓
	//                 create_node(x)
	//                 p = get_node();-------->list_node_allocator::allocate();
	//                 construct(&p->data, x);
	//                       ↓
	//            tmp->next = position.node;
	//            tmp->prev = position.node->prev;
	//            (link_type(position.node->prev))->next = tmp;
	//            position.node->prev = tmp;
	
 
	iterator insert(iterator position, const T& x)
	{
		link_type tmp = create_node(x);   // 产生一个节点
		// 调整双向指针,使tmp插入进去
		tmp->next = position.node;
		tmp->prev = position.node->prev;
		(link_type(position.node->prev))->next = tmp;
		position.node->prev = tmp;
		return tmp;
	}
 
  // 指定位置插入n个值为x的元素, 详细解析见实现部分
  void insert(iterator pos, size_type n, const T& x);
  void insert(iterator pos, int n, const T& x)
  {
	  insert(pos, (size_type)n, x);
  }
  void insert(iterator pos, long n, const T& x)
  {
	  insert(pos, (size_type)n, x);
  }
 
  // 在链表前端插入结点
  void push_front(const T& x) { insert(begin(), x); }
  // 在链表最后插入结点
  void push_back(const T& x) { insert(end(), x); }
 
  // 移除迭代器position所指节点
  iterator erase(iterator position)
  {
	  link_type next_node = link_type(position.node->next);
	  link_type prev_node = link_type(position.node->prev);
	  prev_node->next = next_node;
	  next_node->prev = prev_node;
	  destroy_node(position.node);
	  return iterator(next_node);
  }
 
  // 擦除一个区间的结点, 详细解析见实现部分
  iterator erase(iterator first, iterator last);
 
  void resize(size_type new_size, const T& x);
  void resize(size_type new_size) { resize(new_size, T()); }
  void clear();
 
  // 删除链表第一个结点
  void pop_front() { erase(begin()); }
  // 删除链表最后一个结点
  void pop_back()
  {
	  iterator tmp = end();
	  erase(--tmp);
  }
 
  list(size_type n, const T& value) { fill_initialize(n, value); }
  list(int n, const T& value) { fill_initialize(n, value); }
  list(long n, const T& value) { fill_initialize(n, value); }
 
  ~list()
  {
    // 释放所有结点  // 使用全局函数distance()进行计算, 时间复杂度O(n)
  size_type size() const
  {
    size_type result = 0;
    distance(begin(), end(), result);
    return result;
  }
  clear();
  // 释放头结点
  put_node(node);
  }
 
  list<T, Alloc>& operator=(const list<T, Alloc>& x);
 
protected:
 
	
	// 将[first, last)内的所有元素移动到position之前
	// 如果last == position, 则相当于链表不变化, 不进行操作
	
	// 初始状态
	//                   first                             last
	//                     ↓                                 ↓
	//      --------   --------   --------     --------   --------   --------
	//      | next |-->| next |-->| next |     | next |-->| next |-->| next |
	//  ... --------   --------   -------- ... --------   --------   -------- ...
	//      | prev |<--| prev |<--| prev |     | prev |<--| prev |<--| prev |
	//      --------   --------   --------     --------   --------   --------
	//
	//                           position
	//                               ↓
	//      --------   --------   --------   --------   --------   --------
	//      | next |-->| next |-->| next |-->| next |-->| next |-->| next |
	//  ... --------   --------   --------   --------   --------   -------- ...
	//      | prev |<--| prev |<--| prev |<--| prev |<--| prev |<--| prev |
	//      --------   --------   --------   --------   --------   --------
	//
	// 操作完成后状态
	//                           first
	//                             |
	//               --------------|--------------------------------------
	//               | ------------|------------------------------------ |   last
	//               | |           ↓                                   | |     ↓
	//      -------- | |        --------   --------     --------       | |  --------   --------
	//      | next |-- |  ----->| next |-->| next |     | next |-----  | -->| next |-->| next |
	//  ... --------   |  |     --------   -------- ... --------    |  |    --------   -------- ...
	//      | prev |<---  |  ---| prev |<--| prev |     | prev |<-- |  -----| prev |<--| prev |
	//      --------      |  |  --------   --------     --------  | |       --------   --------
	//                    |  |                                    | |
	//                    |  ------                               | |
	//                    ------- |  ------------------------------ |
	//                          | |  |                              |
	//                          | |  |  -----------------------------
	//                          | |  |  |
	//                          | |  |  |  position
	//                          | |  |  |     ↓
	//      --------   -------- | |  |  |  --------   --------   --------   --------
	//      | next |-->| next |-- |  |  -->| next |-->| next |-->| next |-->| next |
	//  ... --------   --------   |  |     --------   --------   --------   -------- ...
	//      | prev |<--| prev |<---  ------| prev |<--| prev |<--| prev |<--| prev |
	//      --------   --------            --------   --------   --------   --------
	
	void transfer(iterator position, iterator first, iterator last)
	{
		if (position != last)   // 如果last == position, 则相当于链表不变化, 不进行操作
		{
			(*(link_type((*last.node).prev))).next = position.node;
			(*(link_type((*first.node).prev))).next = last.node;
			(*(link_type((*position.node).prev))).next = first.node;
			link_type tmp = link_type((*position.node).prev);
			(*position.node).prev = (*last.node).prev;
			(*last.node).prev = (*first.node).prev;
			(*first.node).prev = tmp;
		}
	}
 
public:
	// 将链表x移动到position所指位置之前
	void splice(iterator position, list& x)
	{
		if (!x.empty())
			transfer(position, x.begin(), x.end());
	}
 
	// 将链表中i指向的内容移动到position之前
	void splice(iterator position, list&, iterator i)
	{
		iterator j = i;
		++j;
		if (position == i || position == j) return;
		transfer(position, i, j);
	}
 
	// 将[first, last}元素移动到position之前
	void splice(iterator position, list&, iterator first, iterator last)
	{
		if (first != last)
			transfer(position, first, last);
	}
 
	void remove(const T& value);
	void unique();
	void merge(list& x);
	void reverse();
	void sort();
 
};
 
// 销毁所有结点, 将链表置空
template <class T, class Alloc>
void list<T, Alloc>::clear()
{
  link_type cur = (link_type) node->next;
  while (cur != node)
  {
    link_type tmp = cur;
    cur = (link_type) cur->next;
    destroy_node(tmp);
  }
  // 恢复node原始状态
  node->next = node;
  node->prev = node;
}
 
// 链表赋值操作
// 如果当前容器元素少于x容器, 则析构多余元素,
// 否则将调用insert插入x中剩余的元素
template <class T, class Alloc>
list<T, Alloc>& list<T, Alloc>::operator=(const list<T, Alloc>& x)
{
  if (this != &x)
  {
    iterator first1 = begin();
    iterator last1 = end();
    const_iterator first2 = x.begin();
    const_iterator last2 = x.end();
    while (first1 != last1 && first2 != last2) *first1++ = *first2++;
    if (first2 == last2)
      erase(first1, last1);
    else
      insert(last1, first2, last2);
  }
  return *this;
}
 
 
// 移除容器内所有的相邻的重复结点
// 时间复杂度O(n)
// 用户自定义数据类型需要提供operator ==()重载
template <class T, class Alloc>
void list<T, Alloc>::unique()
{
  iterator first = begin();
  iterator last = end();
  if (first == last) return;
  iterator next = first;
  while (++next != last)
  {
    if (*first == *next)
      erase(next);
    else
      first = next;
    next = first;
  }
}
 
// 假设当前容器和x都已序, 保证两容器合并后仍然有序
template <class T, class Alloc>
void list<T, Alloc>::merge(list<T, Alloc>& x)
{
  iterator first1 = begin();
  iterator last1 = end();
  iterator first2 = x.begin();
  iterator last2 = x.end();
 
  // 注意:前提是,两个lists都已经递增排序
  while (first1 != last1 && first2 != last2)
    if (*first2 < *first1)
	{
      iterator next = first2;
      transfer(first1, first2, ++next);
      first2 = next;
    }
    else
      ++first1;
  if (first2 != last2)
	  transfer(last1, first2, last2);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

source Insight看STL源码——List 的相关文章

随机推荐

  • 使用线性回归构建波士顿房价预测模型

    使用线性回归构建波士顿房价预测模型 描述 波士顿房价数据集统计了波士顿地区506套房屋的特征以及它们的成交价格 这些特征包括周边犯罪率 房间数量 房屋是否靠河 交通便利性 空气质量 房产税率 社区师生比例 即教育水平 周边低收入人口比例等
  • 机器学习实战—支持向量机

    文章目录 一 简介 1 1 定义 二 线性SVM分类 2 1 简介 2 2 软间隔分类 2 3 初步使用Sk learn接口 三 非线性SVM分类 3 1 简介 3 2 scikit learn实现 3 3 多项式核 解决非线性问题技术一
  • simulink的相关东西

    1 Random Integer Generator M ary 表示 产生的值在0到M 1之间 Sample time 当仿真时间设置为t时 产生的数据量为t sample time samples per frame 以帧为单位输出时
  • 解决虚拟机或物理机ping不通网关故障的方法与思路

    基本思路 确定问题缩小范围 先外部后内部 利用排除法 类比法 替换法 隔离法 将故障范围逐渐缩小到某一点 谨慎做出结论 下结论前先三思 想到所有可能存在问题的点 特别是与别人讨论和描述问题时更应该注意 记录问题 做好文档备案工作 如记录故障
  • vc6中使用com组件

    计算机世界报 第47期 D20 随着Internet和Intranet应用的飞速发展 COM Component Object Model 组件对象模型 以其巨大的潜力渗透到软件学科的各个领域 在Windows操作平台下 众多以COM形式提
  • python之pandas数据导入

    pandas数据导入 学习python最好的学习方法就是带着自己的工作需求或者目标去学习 pandas库不多介绍 先放一些最基础的内容帮助学习 pandas导入 导出数据是非常方便的 可以快速的导入现在常见的excel csv txt 以下
  • JSP之Cookie实现购物车

    1 设置cookie 设置cookie Cookie cookie new Cookie TOM 111 设置有效期 默认秒为单位 cookie setMaxAge 7 24 60 60 添加cookie到客户端 response addC
  • php免杀教程【绝对原创】

    这个绝对原创 首发 个人认为免杀大致分为这几个方面 1 关键字拆分 比如assert 可以写成 a ss e r t 这样 2 可变变量 引用 可变函数 可变变量 比如 a POST x b a eval b 引用 比如 a POST x
  • 吴恩达深度学习课程编程作业(1-2)

    Part 1 Python Basics with Numpy optional assignment 1 Building basic functions with numpy Numpy is the main package for
  • VMware安装Ubuntu18.04

    如下图所示 点击红框部分 创建虚拟机 如下所示 选择自定义 点击下一步 继续下一步 如下所示 红框部分选择Ubuntu18 04的系统镜像 点击下一步 如下所示 填写用户名和密码 点击下一步 自定义修改虚拟机名称和安装位置 或者默认安装 点
  • 使用alibaba的组件sentinel时,测试sentinel dashboard的实时监控功能,在不断刷新页面后,实时监控的图表不显示?

    问题 我是使用的虚拟机开启的sentinel dashboard 虚拟机中的sentinel dashboard一直在运行 nacos也是启动了的 看了一下虚拟机中的sentinel dashboard 发现出现报错信息 提示我连接超时 解
  • MATLAB 基础与通信系统仿真

    文章目录 第 1 章 MATLAB 基础与通信系统仿真 1 1 MATLAB 简介 1 1 1 MATLAB 的起源 1 1 2MATLAB 的特点 1 2 MATLAB 程序设计 1 2 1 MATLAB 工作环境 1 2 2 MATLA
  • Pytorch框架实战——102类花卉分类

    本篇博文为 唐宇迪 计算机视觉实训营第二天 Pytorch框架实战课程的个人笔记 代码来自 qiuzitao深度学习之PyTorch实战 十 与视频教学流程记录一致 课程详情可参考该篇 下文数据集及对应json文件 链接 https pan
  • 人工智能入门路径

    人工智能是一门涵盖广泛 知识面深 应用广泛的技术领域 那么通往人工智能的路径是什么呢 以下是一些入门人工智能的步骤和资源 学习基础数学和编程知识 人工智能涉及到许多数学和计算机科学的基础知识 如线性代数 微积分 概率论 数据结构和算法等 因
  • [人工智能-深度学习-69]:数据集 - 目标检测常见公开数据集之PASCAL VOC

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 122165644 目录 前言 第1章
  • 教妹学Java(十八):break 关键字详解

    你好呀 我是沉默王二 一枚颜值与才华俱在的程序员 本篇教程通过我和三妹对话的形式来谈一谈 Java 中的 break 关键字 教妹学 Java 没见过这么又去的标题吧 语不惊人死不休 没错 标题就是这么酷炫 毕竟面向对象编程 专栏现在定价只
  • qt启动后检测u盘插入和拔掉

    工作中 需要检测u盘的插入和拔掉 其余逻辑可以简化 查了半天 感谢 q62290798 链接在 https blog csdn net q62290798 article details 115869367 utm medium distr
  • 音视频&流媒体的原理以及基础入门知识

    流媒体背景 当下 音视频 流媒体已经无处不在 直播已经火了几年 在后续的时间里面 人们聊天已经不仅仅满足与文字 而是更多的在于 类面对面 交流 能够实时感知对方的表情 动作 为此 有必要跟紧时代潮流 好好梳理梳理流媒体这门功课 流媒体是什么
  • split 分割 字符串(分隔符如:* ^ :

    split 分割 字符串 分隔符如 及注意点 2013 01 05 16 41 41 转载 var tag split 分隔符 java android it var tag split 20998 38548 31526 java and
  • source Insight看STL源码——List

    上一章学vector 不夸张的说大半的时间都在往回找代码上了 不断被typedef不断回看 哪怕可以跳转也找得很累 再一次验证了充分利用工具学习得必要性 接下来要剖析STL源码同时力扣的动态规划现在整理好同时其他分类也要跟上 还有重要的项目