list的使用与迭代器的模拟实现

前面学习了string,vector类的使用及模拟,但是它们有一个统一的特点就是底层的内存是连续的,因此迭代器的实现也很简单。现在我们开始学习list类的使用,模拟实现,来看看这个底层内存不是连续的有什么特别之处!!

list的使用

简单的list类函数这里就不在提了,若那个不太清楚,可以自行查看,或许有意外收获哦~;

list类函数的的使用

底层结构影响了什么

我们知道list的底层是不连续的,就是链表的形式,

我们还知道,迭代器的功能分为

正向迭代器 反向迭代器 固定正向迭代器 和 固定反向迭代器 四种。而 

迭代器的性质也区分

  • 单向迭代器:就能只能++ 或者 --;如:forward_list……
  • 双向迭代器(BidirectionalIterator):既能++ 又能--;如:list,map,set……
  • 随机迭代器(RandomAccessIterator):既能++,又能--,还能 + 或 - ;如:string,vector,deque……

 (+ ,- 指 iterator a + 1或- 1 ,还能指向数据,这些对于底层内存连续的是能够实现的,但是对于内存分散的却不能;++ a  就是只能通过 ++a 才能实现迭代器指向数据的转变,这种不通过 + 1也能实现)

这些是由底层结构实现的,而底层结构又决定了其算法

如:std库里的sort

这里可以解释一下,这个库函数用的快排  利用三数取中 要取随机值;只要随机迭代器能实现。

 std库里的reverse

std库里的generate

 

 

通过这张图我们也能看出,随机是特殊 的双向,双向是特殊的单向,单向是特殊的input output迭代器;这种关系类似于继承的关系,如双向是符,随机是子 

reverse的用途 

其实在list 的reverse是没有必要的;毕竟std库里的revserse都能一样,能完成其能完成的工作,把数据倒置;

emplace back  与 push back 

目前可以认为 emplace back  与 push back 一样,但其实  在一定情况下 emplace back 比 push back好;大多数情况下二者都是差距很小的;

最简单的区别就是他们的用法区别

	list<A> lt;
	A aa1(1, 1);
	lt.push_back(aa1);
	lt.push_back(A(2,2));
	//lt.push_back(3, 3);

	lt.emplace_back(aa1);
	lt.emplace_back(A(2,2));
	cout << endl;
	// 支持直接传构造A对象的参数emplace_back
	lt.emplace_back(3, 3);

 Operations

 这一部分的类函数与前面学习的类函数使用方法稍微有一些不一样;这一部分和二叉树中堆的使用与拓展十分像,底层应该也是用堆实现的;

splice的使用

splice的翻译为汉语是 剪切,粘贴。但其实用法和其是一个意思的,把一个list的某个位置的值剪切到另一个list。

void test1()
{
	list<int> l1(5, 1);
	list<int> l2(5, 2);

	l1.splice(++l1.begin(), l2);
	l1.splice(++l1.begin(), l2, l2.end()--);
	l1.splice(++l1.begin(), l2, ++l2.begin(), l2.end()--);
}

remove,remove_if 

nique 

注意一个前提条件,他的排序必须的有有序的,因此它的描述就有了这一段话

 

// a binary predicate implemented as a function:

bool same_integral_part (double first, double second)
{ return ( int(first)==int(second) ); }

// a binary predicate implemented as a class:

struct is_near {
  bool operator() (double first, double second)
  { return (fabs(first-second)<5.0); }

int main()
{
double mydoubles[]={ 12.15,  2.72, 73.0,  12.77,  3.14,
                       12.77, 73.35, 72.25, 15.3,  72.25 };
  std::list<double> mylist (mydoubles,mydoubles+10);
  
  mylist.sort();             //  2.72,  3.14, 12.15, 12.77, 12.77,
                             // 15.3,  72.25, 72.25, 73.0,  73.35

  mylist.unique();           //  2.72,  3.14, 12.15, 12.77
                             // 15.3,  72.25, 73.0,  73.35

  mylist.unique (same_integral_part);  //  2.72,  3.14, 12.15
                                       // 15.3,  72.25, 73.0

  mylist.unique (is_near());           //  2.72, 12.15, 72.25
}

merge 

 

它的前提条和nique一样,不同的一点是,list  和 参数list 都要是有序的 使用前 要记得,调用其sort。

merge就是有两个排序好的list,把一个list内的数据 有序的插入到另一个list中,(默认是升序)

可以参考代码,自行更深的理解 

bool mycomparison(double first, double second)
{
	return (int(first) < int(second));
}

int main()
{
	std::list<double> first, second;

	first.push_back(3.1);
	first.push_back(2.2);
	first.push_back(2.9);

	second.push_back(3.7);
	second.push_back(7.1);
	second.push_back(1.4);

	first.sort();
	second.sort();

	first.merge(second);

	// (second is now empty)

	second.push_back(2.1);    1.4 2.2 2.9 2.1 3.1 3.7 7.1
}

 sort

默认是升序用的是底层 归并的排序思想。

 可以用放仿函数来改变为降序

list<int> lt;
	lt.push_back(1);
	lt.push_back(20);
	lt.push_back(3);
	lt.push_back(5);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(6);

	//less<int> ls;
	//greater<int> gt;
	//lt.sort(gt);
	
	//方便点直接用匿名函数
	lt.sort(greater<int>());

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

list的模拟实现 

基础成员函数

 

我们知道,链表的组成肯定要有节点,哨兵位。

节点要有一个类list_node,struct默认是public:,因此节点的组成用struct

然后就是list的成员函数的实现要有一个类

迭代器的模拟list_iterator实现要有一个类;

VS编译器底层实现list,也是类似这样的

构造函数的组成

 由于list的构造函数的参数很多,因此可以多设几个重载函数来满足这个需求;

 其中empty_init 的目的是增加可读性,简化步骤,毕竟很多构造函数都需要使用一样的写法;

initializer_list<T> 是要初始化 的类型为 initializer_list 的参数而出现的;注意其使用,不要不会用就行;

void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

		list()
		{
			empty_init();
		}

		//初始化列表 的初始化
		list(initializer_list<T> il)
		{
			empty_init();
			for (auto& e : il)
			{
				push_back(e);
			}
		}

		// lt2(lt1)
		list(const list<T>& lt)
		{
			empty_init();

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

		// lt1 = lt3
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}


		list(size_t n, const T& x = T())
		{
			for (size_t i = 0; i < n; i++)
			{
				push_back(x);
			}
		}

迭代器的实现

 由于list 底层内存不是连续的,其迭代器没变办法像 string vector 一样简单的实现,因此我们要自主实现迭代器;

需要注意的点是,要明白其构造函数是什么,成员函数是什么;

主要内容就是重载 *  ->  ==  != 的符号,让其满足与其他迭代器相同的作用;这里也体现了c++的封装,就算麻烦,也要把它们使用的方法尽量一样;

这里给迭代器类 多个模板参数的原因是 简化其内容,不如写一个 iterator 类 和 const_iterator 类很麻烦。

如何理解?

模板参数就是编译器自主识别是什么类型,然后在list_iterator中 实现;

比如 编译器识别 其类型是 iterator 还是 const iterator,然后有对应的模板和模板参数,然后对于的模板类中,再有就是模板参数代替了原本的 T& T* ,其余都一样。

可以对应这没写多个模板参数的代码在深度理解一下;

 

	template<class T>
	struct list_iterator {
		typedef list_node<T> Node;
		typedef list_iterator<T> self;
		Node* node;

		list_iterator(Node* n)
			:node(n)
		{}

		T& operator*()
		{
			return node->_date;
		}

		T* operator->()
		{
			return &node->_date;
		}

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

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

		bool operator==(const self& i)
		{
			return  node == i.node;
		}

		bool operator!=(const self& i)
		{
			return  node != i.node;
		}
	};

	template<class T>
	struct list_const_iterator {
		typedef list_node<T> Node;
		typedef list_const_iterator<T> self;
		Node* node;

		list_const_iterator(Node* n)
			:node(n)
		{}

		const T& operator*() const
		{
			return node->_date;
		}

		const T* operator->() const
		{
			return &node->_date
		}

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

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

		bool operator==(const self& i)
		{
			return  node == i.node;
		}

		bool operator!=(const self& i)
		{
			return  node != i.node;
		}
	};

 

参考代码 

list.h 

#pragma once

#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;

namespace xryq {
	template <class T>
	struct list_node {
		typedef list_node<T> Node;
		Node* _next;
		Node* _prev;
		T _date;

		//list_node(Node* n)
		//{
		//	_date = n->_date;
		//	_next = n->_next;
		//	_prev = n->_prev;
		//}
		list_node(const T& date = T())
			:_date(date)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	};

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

		ptr operator->()
		{
			return &node->_date;
		}

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

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

		bool operator==(const self& i)
		{
			return  node == i.node;
		}

		bool operator!=(const self& i)
		{
			return  node != i.node;
		}
	};

	//template<class T>
	//struct list_const_iterator {
	//	typedef list_node<T> Node;
	//	typedef list_const_iterator<T> self;
	//	Node* node;

	//	list_const_iterator(Node* n)
	//		:node(n)
	//	{}

	//	const T& operator*() 
	//	{
	//		return node->_date;
	//	}

	//	const T* operator->()
	//	{
	//		return &node->_date
	//	}

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

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

	//	bool operator==(const self& i)
	//	{
	//		return  node == i.node;
	//	}

	//	bool operator!=(const self& i)
	//	{
	//		return  node != i.node;
	//	}
	//};

	template<class T>
	class list {
	public:
		typedef list_node<T> Node;
		typedef list_iterator<T, T&, T*> iterator;
		typedef list_iterator<T, const T&, const T*> const_iterator;

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

		iterator end()
		{

			//return iterator(_head);
			return _head;
		}
		//如果不加 const  就会造成重定义
		const_iterator begin() const
		{
			//iterator it(_head->_next);
			//return it;
			//return iterator(_head->_next);
			return _head->_next;
		}

		const_iterator end() const
		{

			return _head;
		}

		iterator insert(iterator pos, const T& x)
		{
			Node* newnode = new Node(x);

			Node* pre = pos.node->_prev;
			Node* cur = pos.node;

			newnode->_next = cur;
			cur->_prev = newnode;

			newnode->_prev = pre;
			pre->_next = newnode;
			++_size;

			return newnode;
		}

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

			--_size;

			return next;

				
		}

		void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

		list()
		{
			empty_init();
		}

		//初始化列表 的初始化
		list(initializer_list<T> il)
		{
			empty_init();
			for (auto& e : il)
			{
				push_back(e);
			}
		}

		// lt2(lt1)
		list(const list<T>& lt)
		{
			empty_init();

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

		// lt1 = lt3
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}


		list(size_t n, const T& x = T())
		{
			for (size_t i = 0; i < n; i++)
			{
				push_back(x);
			}
		}

		void clear()
		{
			//auto i = begin();
			auto i = begin();
			while (i != end())
			{
				i = erase(i);
			}
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		void push_back(const T& x)
		{
			//Node* tmp = new Node(x);
			//Node* tail = _head->_prev;

			//tmp->_prev = tail;
			//tail->_next = tmp;

			//tmp->_next = _head;
			//_head->_prev = tmp;

			//++_size;

			insert(end(), x);
		}

		void pop_back()
		{
			//assert(!empty());
			//Node* tail = _head->_prev;

			//_head->_prev = tail->_prev;
			//tail->_prev->_next = _head;
			//--_size;

			//delete tail;
			//tail = nullptr;

			erase(--end());
		}

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

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

		void size()
		{
			return _size;
		}

		bool empty()
		{
			return _size == 0;
		}
	private:
		Node* _head = new Node;
		size_t _size = 0;
	};

	template<class Container>
	void print_container(const Container& con)
	{
		// const iterator -> 迭代器本身不能修改
		// const_iterator -> 指向内容不能修改 
		// typename Container::const iterator it = con.begin();   错误的 迭代器无法++
		//typename Container::const_iterator it = con.begin();
		auto it = con.begin();
		while (it != con.end())
		{
			//*it += 10;

			cout << *it << " ";
			++it;
		}
		cout << endl;

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

	struct AA
	{
		int _a1 = 1;
		int _a2 = 1;
	};

	void test_list1()
	{
		list<AA> lta;
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		list<AA>::iterator ita = lta.begin();
		while (ita != lta.end())
		{
			//cout << (*ita)._a1 << endl; 这样方位也行,但是为了方便 地址 直接用  -> 访问可读性更好;
			// vector<T>  也有这种设计
			// 特殊处理,本来应该是两个->才合理,为了可读性,省略了一个->
			cout << ita->_a1 << ":" << ita->_a2 << endl;
			cout << ita.operator->()->_a1 << ":" << ita.operator->()->_a2 << endl;

			++ita;
		}
		cout << endl;
		//	list<int> lta;
		//lta.push_back(1);
		//lta.push_back(1);
		//lta.push_back(1);
		//lta.push_back(1);
	}

	void func(const list<int>& lt)
	{
		print_container(lt);
	}

	void test_list2()
	{
		// 直接构造
		list<int> lt0({ 1,2,3,4,5,6 });
		// 隐式类型转换
		list<int> lt1 = { 1,2,3,4,5,6,7,8 };
		const list<int>& lt3 = { 1,2,3,4,5,6,7,8 };

		func(lt0);
		func({ 1,2,3,4,5,6 });

		print_container(lt1);

		//auto il = { 10, 20, 30 };
	/*	initializer_list<int> il = { 10, 20, 30 };
		cout << typeid(il).name() << endl;
		cout << sizeof(il) << endl;*/
	}

}

 

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值