STL list源码——实现框架、具体实现的详细分段剖析(迭代器的处理、list的实现)、list基本函数总结

list的底层采用的数据结构是环形的双向链表,相对于vector容器的连续线性空间,list插入或删除要付出的代价比vector小很多,对空间的运用有绝对的精准,一点也不浪费。但是list带有链表天生的弱点,就是不支持随机访问。从内置的迭代器角度分析,vector容器对应的迭代器为随机访问迭代器,而list容器内置的迭代器则为双向迭代器
STL中提供的很多算法都是基于随机访问迭代器的,如sort函数使用的迭代器就是随机访问迭代器,因此list不能用这类算法,为此,STL又在list容器内置了其特有的算法

实现框架

1. 节点设计

这是最基础的,链表就是将一个一个的节点通过指针连接起来,节点是构成链表的元素,list的底层结构是环形的双向链表,所以首先得考虑双链表节点的设计:前驱、后继、元素值

template<class T>
struct _list_node
{
	typedef _list_node<T> node_type;//指向本类节点的指针
	node_type *pre;//前驱指针
	node_type *next;//后继指针
	T data;
}

2. 迭代器的设计

list容器对应的迭代器是双向迭代器,既然是双向迭代器,就必须为该迭代器提供++、–的操作,而又由于链表并不一定是连续的空间分配,所以不能直接对原生的指针做++、–的操作,而应该利用迭代器对原生指针做封装,用pre指针和next指针。理解easy

//迭代器`_list_ierator`的设计
template<class T, class Ref = T &, class Ptr = T*>
struct _list_iterator
{
...
}

会给迭代器类型一个别名typedef_list_iterator<T, T&, T*> iterator_type;
…里头的内容👇

//迭代器是个指针,指向节点的指针
//所以先来个指向节点类型的指针
typedef _list_node<T> * link_type;
link_type p;

//迭代器常用的属性们也要安排上, 其中typedef bidirectional_iterator_tag iterator_category;表明这个迭代器是双向迭代器,不支持随机访问

typedef T value_type;//元素类型
typedef Ptr pointer;//指针类型
typedef Ref reference;//引用类型
typedef ptrdiff_t difference_type;//指针差值类型
typedef bidirectional_iterator_tag iterator_category;//标记类型,属于双向迭代器,不支持随机访问
typedef size_t size_type;//大小类型
 

//迭代器的构造函数

_list_iterator(){};//无参构造函数
_list_iterator(link_type p1):p(p1){};//单参构造函数
_list_itrator(const iterator_type &iter):p(iter.p);//拷贝构造函数

//迭代器应有的一些操作(操作符重载)

bool operator== (iterator_type &iter)const;//两个迭代器原生指针是否相等
bool operator != (iterator_type& iter) const;
reference operator*() const;//解引用,返回p->data⭐
pointer operator->()const;//返回地址

//双向容器应有的操作
iterator_type operator++();//定义++前置操作,++iter
iterator_type operator++(int);//定义++后置操作,iter++
iterator_type operator--();//定义--前置操作,--iter
iterator_type operator--(int);//定义--后置操作,iter--

3. list容器的设计

因为list的双向迭代器使得STL的提供的很多算法都不能使用,因此list容器除了内部封装一个迭代器外,还需要提供一些特殊算法
还是一样的流程:最基础的节点类型指针类型、定义公有访问属性、构造函数及析构函数、各种常规操作函数(插入操作、删除操作、大小操作、访问操作)、以及list特有的算法👇

/***************list容器的定义*************************/
template<class T,class Alloc=alloc>
class list
{
	protected:
		typedef _list_node<T> node_type;//定义底层结点类型别名
		typedef _list_node<T>* link_type;//连接底层结点的指针类型别名
		typedef simple_alloc<Node_type,Alloc> node_allocator;//定义结点空间分配器类
		Node_type empty_Node;//定义一个带空节点值的结点
		void init();//创建一个空环形双向链表
	public:
		/**************定义公有访问属性****************/
		typedef T value_type;//底层结点内含的data所对应的数据类型
		typedef value_type* pointer;//指针类型
		typedef value_type& reference;//引用类型
		typedef ptrdiff_t difference_type;//迭代器差值类型
		typedef size_t size_type;//大小类型
		typedef _list_iterator<T,T&,T*> iterator;//迭代器类型
		typedef const iterator const_iterator;//指向常量的迭代器类型 
 
		/*************构造函数/析构函数**************/
		list();//无参构造函数,仅调用init()来初始化链表
		list(InputIterator b,InputIterator e);//用[b,e)去初始化容器
		list(size_type n);//创建一个含有n个元素的容器
		list(size_type n,const T &t);//用n个值为t的元素去创建容器
		~list();//析构函数
 
		/*************插入操作********************/
		void push_back(const T & t);//后插入
		void push_front(const T & t);//前插入
		iterator insert(interator iter,const T &t);//在iter前插入值t的元素,返回新添加元素的迭代器
		void insert(iterator iter,size_type n,const T &t);//在iter前插入n个值为t的元素
		void insert(iterator iter,iterator b,iterator e);//在iter前插入[b,e)范围的元素
 
		/***********删除操作*********************/
		iterator erase(iterator iter);//删除iter所指向的元素,返回所删除元素的下一个元素对应的迭代器
		iterator erase(iterator b,iterator e);//删除[b,e)范围内的元素,返回原先e
		void clear();//删除容器内的所有元素
		void pop_back();//删除容器内最后一个有效的元素
		void pop_front();//删除容器内第一个有效的元素
 
		/***********大小操作*********************/
		size_type size()const;//返回容器内元素的个数
		size_type max_size()const;//返回容器可容纳的最多元素的个数
		bool empty()const;//判断容器是否为空
		void resize(size_type n);//将容器的大小设置为n
		void resize(size_type n,T t);//将容器的大小设置为n,若需要需要新添加新的元素之,则其值为t
 
		/***********访问操作*******************/
		iterator begin();//返回头指针
		iterator end();//返回末端元素的下一个位置
		iterator rbegin();//返回最后一个元素
		iterator rend();//返回头指针的前一个位置
		reference front();//返回第一个元素的引用
		reference back();//返回最后一个元素的引用
 
		/**********list特有的算法操作************/
		void remove(const T& t);//删除之为t的元素
		void remove_if(bool preFun);//将满足特定条件的值删除
		void unique();//将容器中重复的元素删除,只留下第一次出现的那个元素集
		void unique(bool preFun);//将满足条件的重复值删除
		void reverse();//将容器的元素逆转,通过依次将尾部的元素剪切插入到首部
		void sort();//将容器内的元素排序,内部是采用归并排序
		/使用merge函数必须保证连个list是有序的,否则会运行时出错,默认为升序,也可以自己定义降序///
		typedef list<T,alloc> List;//定义本类的类型别名,太长了
		void merge(List &list2);//将list2中的元素剪切后再归并待本链表,升序
		void merge(List &list2,int cmp);//将list归并到本链表,cmp指定排序方式
		void splice(iterator iter,List list2);//将list2的元素剪切到本链表中的iter之前,注意:list2不同于*this
		void splice (iterator iter,List & list2,iterator iter2);//将list2中iter2指向的那一个元素(只有1个哈)剪切到本链表的iter之前,list2可以等于*this
		void splice(iterator iter,List & list2,iterator b,iterator e);//将list2中的[b,e)内的元素剪切到本链表的iter之前,list可以等于*this 
};

把特有的算法再拎出来看一下

/**********list特有的算法操作************/
		void remove(const T& t);//删除之为t的元素
		void remove_if(bool preFun);//将满足特定条件的值删除
		void unique();//将容器中重复的元素删除,只留下第一次出现的那个元素集
		void unique(bool preFun);//将满足条件的重复值删除
		void reverse();//将容器的元素逆转,通过依次将尾部的元素剪切插入到首部
		void sort();//将容器内的元素排序,内部是采用归并排序
		/使用merge函数必须保证连个list是有序的,否则会运行时出错,默认为升序,也可以自己定义降序///
		typedef list<T,alloc> List;//定义本类的类型别名,太长了
		void merge(List &list2);//将list2中的元素剪切后再归并待本链表,升序
		void merge(List &list2,int cmp);//将list归并到本链表,cmp指定排序方式
		void splice(iterator iter,List list2);//将list2的元素剪切到本链表中的iter之前,注意:list2不同于*this
		void splice (iterator iter,List & list2,iterator iter2);//将list2中iter2指向的那一个元素(只有1个哈)剪切到本链表的iter之前,list2可以等于*this
		void splice(iterator iter,List & list2,iterator b,iterator e);//将list2中的[b,e)内的元素剪切到本链表的iter之前,list可以等于*this 
};

源码

节点和迭代器部分源码

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;   //⭐节点node
 
	__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容器部分代码

在之前只是体现了双向链表,并没有体现出双向环状这个特征。在这部分会实现list各个函数的详细实现包括在指定位置插入元素,删除一个区间的元素、移动一个区间的元素及合并两个list的元素等,还有达到双向环状效果的头尾节点的处理

节点特征

  1. 首先链表的最小数据结构要有_list_node类型的节点,要有指针;还有分配节点内存的空间配置器;加上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;//包括迭代器类型

创建节点

  1. 创建链表基础就是创建节点:,对因此节点有创建、删除。代码显示是由全局的constructdestory来构造与析构。注意,产生一个节点,首先分配内存,然后再进行构造
link_type node;//定义好节点,
link_type get_node(){return list_node_allocator::allocate()};//分配一个新节点,但不构造
link_type put_node(link_type  p){link_node_allocator::deallocate(p)};//释放指定节点,但不析构

//创建节点
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);//释放,,why先析构??
}

3. 节点搞定,轮到链表

###①空链表的建立,让node头尾都指向自己,空链表不设元素值

void empty_initialize()
	{
		node = get_node();   // 配置一个节点空间,令node指向它
		node->next = node;   // 令node头尾都指向自己,不设元素值
		node->prev = node;
	}

②创建值为value的n个节点的链表,

创建空链表,再insert元素。带回退操作

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));
	}

③list的简单函数

   list() { empty_initialize(); }
  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); }
  //↑构造函数们
	iterator begin() { return (link_type)((*node).next); }//因为第一个node是空节点,即head,是没有元素值的
	iterator end() { return node; }// 链表成环, 当指所以头节点也就是end
	bool empty() const { return node->next == node; }// 头结点指向自身说明链表中无元素
	size_type size() const
	{
		size_type result = 0;
		distance(begin(), end(), result);//distance()两个迭代器之间的距离
		return result;
	}
 
	size_type max_size() const { return size_type(-1); }
	reference front() { return *begin(); }
	reference back() { return *(--end()); }
 

④insert()函数的实现:

在指定位置插入元素:

iterator insert(iterator position, const T& x)
	{
		link_type tmp = create_node(x);   // 产生一个节点,p = get_node(), construct(&p->data, x);

		// 调整双向指针, 
		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); }

⑤erase函数的实现

移除迭代器position所指节点,记得删除原position所指元素,依旧返回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 pop_front() { erase(begin()); }
  void pop_back()
  {
	  iterator tmp = end();
	  erase(--tmp);
  }

⑥析构函数,clear函数

要释放所有的节点,先返回长度,再调用clear函数,最后释放头节点put_node(node)

~list()
  {
    // 释放所有结点  // 使用全局函数distance()进行计算, 时间复杂度O(n)
  size_type size() const
  {
    size_type result = 0;
    distance(begin(), end(), result);
    return result;
  }
  clear();
  // 释放头结点
  put_node(node);
  }

clear函数:

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;//所以在析构函数中释放头节点是另做的
}

⑦transfer函数

将[first, last)内的所有元素移动到position之前,如果last == position, 则相当于链表不变化, 不进行操作
注意范围前闭后开
要处理的当然有position位置前与待移动区域的连接,还有缺了first、last原来那块地方的连接first.pre.next = last

void transfer(iterator position, iterator first, iterator last)
	{
		if (position != last)   
		{
			(*(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;
		}
	}

⑧splice函数, 借用transfer函数

splice是list中特有的拼接方法,主要是用来合并两个list,splice实现了不需要拷贝的list合并,即可以在常数时间内从list的一个区域拼接到另一个list的一个区域。也就是说splice是一个常数时间的函数

// 将链表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);//前闭后开就是i这个迭代器所指的元素√
	}
 
	// 将[first, last}元素移动到position之前
	void splice(iterator position, list&, iterator first, iterator last)
	{
		if (first != last)
			transfer(position, first, last);
	}

⑨unique函数

unique函数,移除容器内所有的相邻的重复节点用户自定义数据类型需要提供operator 等号()重载 operator ==()

//unique代码核心:不管相不相等(不相等就把next当前元素erase掉),erase都要回到first上
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;
  }
}
 

⑩merge函数

合并两个有序链表

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小的话移动到first1前面
      first2 = next;
    }
    else
      ++first1;//否则,1往前
  //有一个链表结束了,如果2结束了那就没事了,1结束了那就把2放到1后面去
  if (first2 != last2)
	  transfer(last1, first2, last2);
}

重载等号操作符, 链表赋值操作

如果当前容器元素少于x容器, 则析构多余元素,否则将调用insert插入x中剩余的元素

// 链表赋值操作
// 如果当前容器元素少于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;
}
 /*有点不知道这个函数在干嘛,把链表2中的元素一一赋值到链表1,直到两个链表中某一个链表已经遍历完,
 那么就判断是不是链表2遍历完了,是的话,就把链表1的整个范围给删掉?
 否则就是链表1遍历完了,链表2的内容多一些咯,就把2中元素复制到last前???
 这是什么操作????*/

anyway正经第二篇stl,开心~~

list常用函数总结

Lst1.assign() 给list赋值
Lst1.back() 返回最后一个元素
Lst1.begin() 返回指向第一个元素的迭代器
Lst1.clear() 删除所有元素
Lst1.empty() 如果list是空的则返回true
Lst1.end() 返回末尾的迭代器
Lst1.erase() 删除一个元素
Lst1.front() 返回第一个元素
Lst1.get_allocator() 返回list的配置器
Lst1.insert() 插入一个元素到list中
Lst1.max_size() 返回list能容纳的最大元素数量
Lst1.merge() 合并两个list
Lst1.pop_back() 删除最后一个元素
Lst1.pop_front() 删除第一个元素
Lst1.push_back() 在list的末尾添加一个元素
Lst1.push_front() 在list的头部添加一个元素
Lst1.rbegin() 返回指向第一个元素的逆向迭代器
Lst1.remove() 从list删除元素
Lst1.remove_if() 按指定条件删除元素
Lst1.rend() 指向list末尾的逆向迭代器
Lst1.resize() 改变list的大小
Lst1.reverse() 把list的元素倒转
Lst1.size() 返回list中的元素个数
Lst1.sort() 给list排序
Lst1.splice() 合并两个list
Lst1.swap() 交换两个list
Lst1.unique() 删除list中相邻重复的元素

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值