关于标准库中的list(涉及STL的精华-迭代器的底层)

目录

关于list

list常见接口实现

          STL的精华之迭代器


关于list

list的文档介绍

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

常见接口:

关于标准库中的list(涉及STL的精华-迭代器的底层)_第1张图片


list常见接口实现(参考部分g++版本下stl底层源码)

namespace dw
{
	template
	struct list_node //链表节点
	{
		list_node* _prev;
		list_node* _next;
		T _data;
	};

	//迭代器实现

	template
	class list
	{
		typedef list_node node;
	public:
		//代码实现


	private:
		node* _head;
	};

	//------------------------------------------------
	void list_test()
	{
		;
	}

}

1.注意:类名不是类型,建议声明类型的时候都加上模板参数

举例来说这里 list_node 类名  list_node 类型

如果不加上模板参数运行程序会报错

typedef list_node node;
typedef list_node node;

关于标准库中的list(涉及STL的精华-迭代器的底层)_第2张图片

2.注意:这里使用的 struct  定义类,struct 定义的类默认访问权限是公开。

构造函数

无参

		void empty_init() //初始化头节点
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		list()
		{
			empty_init();
		}

push_back

		void push_back(const T& x)
		{
			node* tail = _head->_prev;
			node* newnode = new node(x);

			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;

		}

注意,这里测试运行会发现:

关于标准库中的list(涉及STL的精华-迭代器的底层)_第3张图片

原因出在:

node* newnode = new node(x);

所以关于链表节点 list_node 的这个类也需要实现构造函数

		list_node(const T& x = T()) //这里是匿名对象调用构造函数
			:_prev(nullptr)
			,_next(nullptr)
			,_data(x)
		{}


   

 STL的精华之迭代器

         这样就完成了需要的准备工作,接下来就可以进行神奇的迭代器部分了,看完大呼 - 还可以这样玩。

         上面的代码可以通过测试以及调试观看到具体状态,那么怎么进行成员访问?

         我们知道,vector 迭代器使用的是原生指针(g++版本) vector相关迭代器的实现,因为 vector 可以看作是一块连续的物理空间,我们通过下标就可以访问到下一个元素

       但是链表可以这样玩吗?肯定是不可以的!所以,这里 list 迭代器的实现不能使用原生指针,而是需要一个类去进行封装。

关于标准库中的list(涉及STL的精华-迭代器的底层)_第4张图片

iterator

//迭代器实现
	template
	struct __list_iterator
	{
	public:
		typedef list_node node;

		typedef __list_iterator self;

		node* _node;

		__list_iterator(node* x)
			:_node(x)
		{}

		T& operator*()
		{
			return _node->_data;
		}

		self& operator++() 
		{
			_node =  _node->_next;
			return *this;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

	};

注意:c++一般不喜欢内部类,所以一般都使用自定义类型

继而我们需要在 list 类进行 typedef 

typedef __list_iterator iterator;

begin()

begin()是第一个节点的位置

        iterator begin()
		{
			iterator tmp(_head->_next);
			return tmp;
		}

		

end()

end()是头节点的位置

        iterator end()
		{
			return iterator(_head);
		}

注:这里和 begin( ) 的写法其实没什么不同,本质上是运用了匿名对象。

关于标准库中的list(涉及STL的精华-迭代器的底层)_第5张图片

  •         其实这里可以看到,虽然vector,list 表面上都是 iterator,但是底层却千差万别,这些都源自于底层的封装

  •         并且,可以明显的感觉到迭代器很好体现了类的设计价值。如果一个内置类型无法满足我们的需求,那么我们可以使用一个自定义类型去封装,然后重载运算符,继而改变它的行为

  •         比如说这里的 iterator 是一个节点的指针,++* 不满足我们的需求,我们可以去进行封装, 用类去封装一个node* 重载++, * 运算符的函数

  •         重载运算符函数的具体实现以及行为完全由我们自己来定义,这是自定义类型+运算符重载+类的定义等等的价值

        其次,关于下面这句代码,我们并没有实现拷贝构造,编译器会默认生成,默认生成的拷贝构造是浅拷贝,那么,这里可以使用浅拷贝嘛?为什么没报错?

list::iterator it = lt.begin();

//首先:这里是需要浅拷贝的,因为这里拷贝的是指向节点的指针

//其次,为什么没报错 ? 是因为迭代器没有写析构函数,那么为什么没写 ? 

//是因为迭代器不需要释放节点。更深层次一些,为什么不需要释放节点 ?

//虽然这里迭代器有指向节点的指针,但是并不支持释放节点,因为释放节点是链表的行为

//链表会有析构函数释放节点,也可以简单把这里迭代器理解为工具,可以支持读或者写

//但是只有使用权限,并没有归属权限,所以这里浅拷贝也就没有问题了。

然后现在就可以丰富一些 迭代器 __list_iterator 这个类,后置 ++ ,--,等等

template
	struct __list_iterator
	{
	public:
		typedef list_node node;

		typedef __list_iterator self;

		node* _node;

		__list_iterator(node* x)
			:_node(x)
		{}

		T& operator*()
		{
			return _node->_data;
		}

		self& operator++()
		{
			_node =  _node->_next;
			return *this;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		self& operator++(int) //编译器会默认传一个整型,进行占位,更前置进行区分
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		self& operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}

	};

const_iterator

接下来是关于 const 修饰的迭代器,如果是下面这样环境,该怎么办?

关于标准库中的list(涉及STL的精华-迭代器的底层)_第6张图片

这里的迭代器就需要使用const 进行修饰了,那么请问这样写法可不可以?

		iterator begin() const
		{
			iterator tmp(_head->_next);
			return tmp;
		}

		iterator end() const
		{
			return iterator(_head);
		}

这里运行编译通过,但是存在一个问题,明明是const修饰,为什么还能构造迭代器?
因为这里的const修饰的是*this 也就是指向的内容,*this 是这个节点的指针,const修饰的是这个指针的本身不能被改变,也就是_head不能被改变,但是可以拷贝。

关于标准库中的list(涉及STL的精华-迭代器的底层)_第7张图片
结果发现并不符合我们的预期,因为这里我们期望迭代器被 const 修饰之后内容不可修改。这里不仅可读,并且可写,显然程序是有些不正确的。

所以这里的写法是我们需要再完成一个类,也就是 __list_const_iterator

    template
	struct __list_const_iterator
	{
	public:
		typedef list_node node;

		typedef __list_const_iterator self;

		node* _node;

		__list_const_iterator(node* x)
			:_node(x)
		{}

		const T& operator*()
		{
			return _node->_data;
		}

		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		self& operator++(int) 
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		self& operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}

	};

注意:c++一般不喜欢内部类,所以一般都使用自定义类型

继而我们需要在 list 类进行 typedef 

typedef __list_const_iterator const_iterator;

begin()

        const_iterator begin() const
		{
			const_iterator tmp(_head->_next);
			return tmp;
		}

end()

        const_iterator end() const
		{
			return const_iterator(_head);
		}

代码程序正常运行

关于标准库中的list(涉及STL的精华-迭代器的底层)_第8张图片

但是观察上面这两个类我们会发现,__list_iterator  __list_const_iterator

他们的区别只在于重载运算符解引用的实现不同,更细节一点只是解引用的返回值不同

__list_iterator 

关于标准库中的list(涉及STL的精华-迭代器的底层)_第9张图片

 __list_const_iterator

关于标准库中的list(涉及STL的精华-迭代器的底层)_第10张图片

        但是却写了两个类,这样会显得代码很臃肿,会让人觉得一模一样的代码为什么要写两遍,所以这里提出了一个新的语法知识 -  -  - 添加模板参数

代码如下:

    template
	struct __list_iterator
	{
	public:
		typedef list_node node;

		typedef __list_iterator self;

		node* _node;

		__list_iterator(node* x)
			:_node(x)
		{}

		Ref operator*()
		{
			return _node->_data;
		}

		self& operator++()
		{
			_node =  _node->_next;
			return *this;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		self& operator++(int) //编译器会默认传一个整型,进行占位,更前置进行区分
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		self& operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}

	};

这样 __list_iterator  __list_const_iterator 就可以合并为一个类

关于标准库中的list(涉及STL的精华-迭代器的底层)_第11张图片

再修改一些 list ,模板参数不同,调用逻辑也不同

        typedef __list_iterator iterator;

		typedef __list_iterator const_iterator;

		//typedef __list_const_iterator const_iterator; //注释掉

例:

关于标准库中的list(涉及STL的精华-迭代器的底层)_第12张图片

       如果你认为到这里就结束了,那么不好意思,还差一点。因为观察库里 list 的实现,我们会发现迭代器的模板参数是三个。

附上stl部分底层源码:

template
struct __list_iterator {
  typedef __list_iterator             iterator;
  typedef __list_iterator const_iterator;
  typedef __list_iterator           self;

  typedef bidirectional_iterator_tag iterator_category;
  typedef T value_type;
  typedef Ptr pointer;
  typedef Ref reference;
  typedef __list_node* link_type;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;

  link_type node;

  __list_iterator(link_type x) : node(x) {}
  __list_iterator() {}
  __list_iterator(const iterator& x) : node(x.node) {}

  bool operator==(const self& x) const { return node == x.node; }
  bool operator!=(const self& x) const { return node != x.node; }
  reference operator*() const { return (*node).data; }

#ifndef __SGI_STL_NO_ARROW_OPERATOR
  pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */

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

源码的  Ptr 是什么?因为这里不仅重载了*,还重载了 ->,那么什么时候要去调用->呢?

1.迭代器要么就是原生指针
⒉.迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为

解释这个原因的话,先看一个测试用例:

关于标准库中的list(涉及STL的精华-迭代器的底层)_第13张图片

这时候我们会发现,之所以会报错是因为AA这个类没有自己实现一个流插入

所以要是想让代码跑起来,有很多的解决办法

方法一便是根据AA这个类型重载一个流插入
关于标准库中的list(涉及STL的精华-迭代器的底层)_第14张图片

所以回过头来也能发现,c++新增运算符重载,而不是继续使用printf函数,是因为printf函数有局限性,printf只能打印内置类型,%d,%lf等等。
关于标准库中的list(涉及STL的精华-迭代器的底层)_第15张图片

但是打印也不是没有其他办法,比如说上面这种,但是看到似乎是有点怪,解引用之后去访问成员
所以这样为了看起来更顺畅一些,我们需要去实现  ->
关于标准库中的list(涉及STL的精华-迭代器的底层)_第16张图片

这里返回值是T*,但是如果是const迭代器呢?

所以这里就不能使用T*,而是需要新增加函数模板参数 Ptr

	template
	struct __list_iterator
	{
	public:
		typedef list_node node;

		typedef __list_iterator self;

		node* _node;

		__list_iterator(node* x)
			:_node(x)
		{}

		Ptr operator->()
		{
			return &_node->_data;
		}

		Ref operator*()
		{
			return _node->_data;
		}

		self& operator++()
		{
			_node =  _node->_next;
			return *this;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		self& operator++(int) 
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		self& operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}

	};


关于标准库中的list(涉及STL的精华-迭代器的底层)_第17张图片

关于标准库中的list(涉及STL的精华-迭代器的底层)_第18张图片

以上就是list底层源码的实现逻辑,补充一点:

关于标准库中的list(涉及STL的精华-迭代器的底层)_第19张图片

这里看着是有些怪异的,因为

it-> _a1
it-> ->_a1

本来这里应该是两个 ->

一个是运算符重载的调用

一个是有了结构体的指针再使用 -> 去访问

这里为了增加代码的可读性,省略了一个-> ,可以理解为是一个特殊处理

看似是:

cout << it->_a1 << ":" << it->_a2 << endl;

实际上:

cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << endl;

另外需要说明的点是:

迭代器用原生指针只是一个偶然,用类去封装才是一个常态

但是底层的本质都可以认为是指针,只说是嵌入了一个自定义类型去封装指针,在编译器看来是自定义类型而不是指针

并且自定义类型使用运算符只能去重载运算符,至于重载运算符函数的行为,完全是由我们自己来控制的

包括vector的迭代器,  在g++版本下(linux系统)是原生指针,但是vs下也不是原生指针,因为vs需要重载运算符函数,比如 * 用来判断迭代器是否失效。

要注意,不同编译器底层实现不同。以上就是 stl 的精华部分,关于迭代器。
       


swap

		void swap(list& tmp)
		{
			std::swap(_head, tmp._head);
		}

迭代器区间构造

        template
		list(Iterator first, Iterator last)
		{
			empty_init();

			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

拷贝构造

        //现代写法
		list(const list& lt)
		{
			empty_init();

			list tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		//传统写法
		list(const list& lt)
		{
			empty_init();

			for (auto e : lt)
			{
				push_back(e);
			}
		}

赋值

		list& operator=(list lt)
		{
			swap(lt);
			return *this;
		}

insert

		void insert(iterator pos, const T& x)
		{
			node* cur = pos._node;
			node* prev = cur->_prev;

			node* new_node = new node(x);

			prev->_next = new_node;
			new_node->_next = cur;
			new_node->_prev = prev;
			cur->_prev = new_node;
		}

erase

		void erase(iterator pos)
		{
			assert(pos != end());
			node* next = pos._node->_next;
			node* prev = pos._node->_prev;

			next->_prev = prev;
			prev->_next = next;
			delete pos._node;
		}

        //看需求
        iterator erase(iterator pos)
		{
			assert(pos != end());
			node* next = pos._node->_next;
			node* prev = pos._node->_prev;

			next->_prev = prev;
			prev->_next = next;
			delete pos._node;

			return iterator(next);
		}

push_front

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

pop_back

		void pop_back()
		{
			erase(--end());
		}

pop_front

		void pop_front()
		{
			erase(begin());
		}

clear

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				erase(it++);
				
				//it = erase(it);  //两种方法都可以,这中 erase 需要有返回值
			}
		}

析构函数

		~list()
		{
			clear();

			delete _head;
			_head = nullptr;

		}

最后附上全部代码以及测试用例:

                                                                                 list.h


namespace dw
{
	template
	struct list_node //链表节点
	{
		list_node* _prev;
		list_node* _next;
		T _data;

		list_node(const T& x = T())
			:_prev(nullptr)
			,_next(nullptr)
			,_data(x)
		{}
	};



	//迭代器实现

	template
	struct __list_iterator
	{
	public:
		typedef list_node node;

		typedef __list_iterator self;

		node* _node;

		__list_iterator(node* x)
			:_node(x)
		{}

		Ptr operator->()
		{
			return &_node->_data;
		}

		Ref operator*()
		{
			return _node->_data;
		}

		self& operator++()
		{
			_node =  _node->_next;
			return *this;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		self& operator++(int) //编译器会默认传一个整型,进行占位,更前置进行区分
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		self& operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}

	};
	

	/*
	template
	struct __list_const_iterator
	{
	public:
		typedef list_node node;

		typedef __list_const_iterator self;

		node* _node;

		__list_const_iterator(node* x)
			:_node(x)
		{}

		const T& operator*()
		{
			return _node->_data;
		}

		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		self& operator++(int) 
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		self& operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}

	};
	*/


	template
	class list
	{
		typedef list_node node;
	public:
		typedef __list_iterator iterator;

		typedef __list_iterator const_iterator;

		//typedef __list_const_iterator const_iterator;

		void empty_init() //初始化头节点
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		list()
		{
			empty_init();
		}

	
		void swap(list& tmp)
		{
			std::swap(_head, tmp._head);
		}

		//现代写法
		list(const list& lt)
		{
			empty_init();

			list tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		//传统写法
		//list(const list& lt)
		//{
		//	empty_init();

		//	for (auto e : lt)
		//	{
		//		push_back(e);
		//	}
		//}

		template
		list(Iterator first, Iterator last)
		{
			empty_init();

			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

		//lt2 = lt1
		list& operator=(list lt)
		{
			swap(lt);
			return *this;
		}

		iterator begin() 
		{
			iterator tmp(_head->_next);
			return tmp;
		}

		iterator end() 
		{
			return iterator(_head);
		}

		const_iterator begin() const
		{
			const_iterator tmp(_head->_next);
			return tmp;
		}

		const_iterator end() const
		{
			return const_iterator(_head);
		}

		void push_back(const T& x)
		{
			node* tail = _head->_prev;
			node* newnode = new node(x);

			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;

		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());
			node* next = pos._node->_next;
			node* prev = pos._node->_prev;

			next->_prev = prev;
			prev->_next = next;
			delete pos._node;

			return iterator(next);
		}

		void insert(iterator pos, const T& x)
		{
			node* cur = pos._node;
			node* prev = cur->_prev;

			node* new_node = new node(x);

			prev->_next = new_node;
			new_node->_next = cur;
			new_node->_prev = prev;
			cur->_prev = new_node;
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				erase(it++);
				
				//it = erase(it);  //两种方法都可以,这中 erase 需要有返回值
			}
		}

		~list()
		{
			clear();

			delete _head;
			_head = nullptr;

		}


	private:
		node* _head;
	};




	//------------------------------------------------

	void print_list(const list& lt)
	{
		list::const_iterator it = lt.begin();
		while (it != lt.end())
		{
			//(*it) *= 2;
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}


	void list_test1()
	{
		list lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		list::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		print_list(lt);

		list lt2(lt);

		for (auto e : lt2)
		{
			cout << e << " ";
		}
		cout << endl;


		list lt3 = lt2;

		for (auto e : lt3)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void list_test2()
	{
		list lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		lt.push_back(1000); //测试尾插
		lt.push_front(100); // 测试头插

		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		lt.pop_back(); // 测试尾删
		lt.pop_front(); //测试头删

		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		auto pos = lt.begin();
		++pos;

		lt.insert(pos, 9); //测试任意位置插入
		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		pos = lt.begin();
		++pos;

		lt.erase(pos); // 测试任意位置删除
		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

	}

}

你可能感兴趣的:(c++,c++,开发语言)