【C++】list用法&&简单模拟实现

在这里插入图片描述


1. list的介绍及使用

list的文档介绍】

1.1 list基本概念

功能:将数据进行链式存储

链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的

链表的组成:链表由一系列结点组成

结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

STL中的链表是一个双向循环链表

image-20230106100428871

由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器

list的优点:

  • 采用动态存储分配,不会造成内存浪费和溢出
  • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素

list的缺点:

  • 链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大

List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。

总结:STL中List和vector是两个最常被使用的容器,各有优缺点


1.2 list的构造

功能描述:

  • 创建list容器
构造函数( (constructor)) 接口说明
list (size_type n, const value_type& val = value_type()) 构造的list中包含n个值为val的元素
list() 构造空的list
list (const list& x) 拷贝构造函数
list (InputIterator first, InputIterator last) 用[first, last)区间中的元素构造list

示例

// list的构造
void TestList1()
{
    
    
	list<int> l1;                         // 构造空的l1
	list<int> l2(4, 100);                 // l2中放4个值为100的元素
	list<int> l3(l2.begin(), l2.end());  // 用l2的[begin(), end())左闭右开的区间构造l3
	list<int> l4(l3);                    // 用l3拷贝构造l4

	// 以数组为迭代器区间构造l5
	int array[] = {
    
     16, 2, 77, 29 };
	list<int> l5(array, array + sizeof(array) / sizeof(int));

	// 列表格式初始化C++11
	list<int> l6{
    
     1, 2, 3, 4, 5 };

	// 用迭代器方式打印l5中的元素
	list<int>::iterator it = l5.begin();
	while (it != l5.end())
	{
    
    
		cout << *it << " ";
		++it;
	}
	cout << endl;

	// C++11范围for的方式遍历
	for (auto& e : l5)
		cout << e << " ";

	cout << endl;
}
image-20230106101258360

提醒:list构造方式同其他几个STL常用容器,熟练掌握其中几个常用的即可


1.3 list的迭代器使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点

list的迭代器跟vector的迭代器并非一样是原生指针,具体是什么,后面模拟实现就清楚了

函数声明 接口说明
begin + end 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置

【注意】

  1. beginend为正向迭代器,对迭代器执行**++**操作,迭代器向后移动

  2. **rbegin(end)rend(begin)为反向迭代器,对迭代器执行++**操作,迭代器向前移动

示例

// list迭代器的使用
// 注意:遍历链表只能用迭代器和范围for
void PrintList(const list<int>& l)
{
    
    
	// 注意这里调用的是list的 begin() const,返回list的const_iterator对象
	for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
	{
    
    
		cout << *it << " ";
		// *it = 10; 编译不通过
	}

	cout << endl;
}

void TestList2()
{
    
    
	int array[] = {
    
     1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	list<int> l(array, array + sizeof(array) / sizeof(array[0]));
	// 使用正向迭代器正向list中的元素
	// list<int>::iterator it = l.begin();   // C++98中语法
	auto it = l.begin();                     // C++11之后推荐写法
	while (it != l.end())
	{
    
    
		cout << *it << " ";
		++it;
	}
	cout << endl;

	// 使用反向迭代器逆向打印list中的元素
	// list<int>::reverse_iterator rit = l.rbegin();
	auto rit = l.rbegin();
	while (rit != l.rend())
	{
    
    
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}
image-20230106101735070

1.4 list 赋值和交换

功能描述:

  • 给list容器进行赋值,以及交换list容器
函数原型 接口说明
assign(beg, end) 将[beg, end)区间中的数据拷贝赋值给本身
assign(n, elem) 将n个elem拷贝赋值给本身
list& operator=(const list &lst) 重载等号操作符
swap(lst) 将lst与本身的元素互换。

示例

// 注意:遍历链表只能用迭代器和范围for
void PrintList(const list<int>& l)
{
    
    
	// 注意这里调用的是list的 begin() const,返回list的const_iterator对象
	for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
	{
    
    
		cout << *it << " ";
		// *it = 10; 编译不通过
	}

	cout << endl;
}


//赋值和交换
void TestList3()
{
    
    
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);
	PrintList(L1);

	//赋值
	list<int>L2;
	L2 = L1;
	PrintList(L2);

	list<int>L3;
	L3.assign(L2.begin(), L2.end());
	PrintList(L3);

	list<int>L4;
	L4.assign(10, 100);
	PrintList(L4);

}

//交换
void TestList4()
{
    
    

	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	list<int>L2;
	L2.assign(10, 100);

	cout << "交换前: " << endl;
	PrintList(L1);
	PrintList(L2);

	cout << endl;

	L1.swap(L2);

	cout << "交换后: " << endl;
	PrintList(L1);
	PrintList(L2);

}
image-20230106102612931 image-20230106102629648

1.5 list 插入和删除

功能描述:

  • 对list容器进行数据的插入和删除
函数声明 接口说明
push_front 在list首元素前插入值为val的元素
pop_front 删除list中第一个元素
push_back 在list尾部插入值为val的元素
pop_back 删除list中最后一个元素
insert 在list position 位置中插入值为val的元素
erase 删除list position位置的元素
swap 交换两个list中的元素
clear 清空list中的有效元素

示例

// list插入和删除
// push_back/pop_back/push_front/pop_front
void TestList5()
{
    
    
	int array[] = {
    
     1, 2, 3 };
	list<int> L(array, array + sizeof(array) / sizeof(array[0]));

	// 在list的尾部插入4,头部插入0
	L.push_back(4);
	L.push_front(0);
	PrintList(L);

	// 删除list尾部节点和头部节点
	L.pop_back();
	L.pop_front();
	PrintList(L);
}

image-20230106103518520
// insert /erase 
void TestList6()
{
    
    
	int array1[] = {
    
     1, 2, 3 };
	list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));

	// 获取链表中第二个节点
	auto pos = ++L.begin();
	cout << *pos << endl;

	// 在pos前插入值为4的元素
	L.insert(pos, 4);
	PrintList(L);

	// 在pos前插入5个值为5的元素
	L.insert(pos, 5, 5);
	PrintList(L);

	// 在pos前插入[v.begin(), v.end)区间中的元素
	vector<int> v{
    
     7, 8, 9 };
	L.insert(pos, v.begin(), v.end());
	PrintList(L);

	// 删除pos位置上的元素
	L.erase(pos);
	PrintList(L);

	// 删除list中[begin, end)区间中的元素,即删除list中的所有元素
	L.erase(L.begin(), L.end());
	PrintList(L);
}

image-20230106103538833
// resize/swap/clear
void TestList7()
{
    
    
	// 用数组来构造list
	int array1[] = {
    
     1, 2, 3 };
	list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));
	PrintList(l1);

	// 交换l1和l2中的元素
	list<int> l2;
	l1.swap(l2);
	PrintList(l1);
	PrintList(l2);

	// 将l2中的元素清空
	l2.clear();
	cout << l2.size() << endl;
}
image-20230106103556897

1.6 list容量大小操作

功能描述:

  • 对list容器的大小进行操作
函数声明 接口说明
empty 检测list是否为空,是返回true,否则返回false
size 返回list中有效节点的个数
resize(num,elem) 重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除

示例

//大小操作
void TestList8()
{
    
    
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	if (L1.empty())
	{
    
    
		cout << "L1为空" << endl;
	}
	else
	{
    
    
		cout << "L1不为空" << endl;
		cout << "L1的大小为: " << L1.size() << endl;
	}

	//重新指定大小
	L1.resize(10);
	PrintList(L1);

	L1.resize(2);
	PrintList(L1);
}
image-20230106104259886

1.7 list 数据存取

功能描述:

  • 对list容器中数据进行存取
函数声明 接口说明
front 返回list的第一个节点中值的引用
back 返回list的最后一个节点中值的引用

示例

//数据存取
void TestList9()
{
    
    
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);


	//cout << L1.at(0) << endl;//错误 不支持at访问数据
	//cout << L1[0] << endl; //错误  不支持[]方式访问数据
	cout << "第一个元素为: " << L1.front() << endl;
	cout << "最后一个元素为: " << L1.back() << endl;

	//list容器的迭代器是双向迭代器,不支持随机访问
	list<int>::iterator it = L1.begin();
	//it = it + 1;//错误,不可以跳跃访问,即使是+1
}
image-20230106104439866

总结:

  • list容器中不可以通过[]或者at方式访问数据
  • 返回第一个元素 — front
  • 返回最后一个元素 — back

2. list的模拟实现

这次要模拟实现的类及其成员函数接口总览

namespace hdm
{
    
    
	//模拟实现list当中的结点类
	template<class T>
	struct ListNode
	{
    
    
		//成员函数
		ListNode(const T& val = T()); //构造函数

		//成员变量
		T _val;                 //数据域
		ListNode<T>* _next;   //后继指针
		ListNode<T>* _prev;   //前驱指针
	};

	//模拟实现list迭代器
	template<class T, class Ref, class Ptr>
	struct ListIterator
	{
    
    
		typedef ListIterator<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;

		ListIterator(node* pnode);  //构造函数

		//各种运算符重载函数
		Self operator++();
		Self operator--();
		Self operator++(int);
		Self operator--(int);
		bool operator==(const Self& s) const;
		bool operator!=(const Self& s) const;
		Ref operator*();
		Ptr operator->();

		//成员变量
		Node* _node; //一个指向结点的指针
	};

	//模拟实现list
	template<class T>
	class list
	{
    
    
	public:
		typedef ListNode<T> Node;
		// 正向迭代器
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

		// 反向迭代器
		typedef ReverseIterator<iterator> reverse_iterator;
		typedef ReverseIterator<const_iterator> const_reverse_iterator;

		//默认成员函数
		list();
		list(const list<T>& lt);
		list<T>& operator=(const list<T>& lt);
		~list();

		//迭代器相关函数
		iterator begin();
		iterator end();
		const_iterator begin() const;
		const_iterator end() const;
        //反向迭代器
        reserve_iterator rbegin();
        reserve_iterator rend();
        const_reserve_iterator rbegin()const;
        const_reserve_iterator rend();const;

		//访问容器相关函数
		T& front();
		T& back();
		const T& front() const;
		const T& back() const;

		//插入、删除函数
		void insert(iterator pos, const T& x);
		iterator erase(iterator pos);
		void push_back(const T& x);
		void pop_back();
		void push_front(const T& x);
		void pop_front();

		//其他函数
		size_t size() const;
		void resize(size_t n, const T& val = T());
		void clear();
		bool empty() const;
		void swap(list<T>& lt);

	private:
		Node* _head; //指向链表头结点的指针
	};
}

2.1 结点类的实现

我们在之前的章节中曾经讲述过并且也模拟实现过纯C语言版本,C++中STL标准库中的list用的是双向循环链表,它的结构看似复杂,功能却是最好,最容易实现的

image-20230109151213943

我们若要实现list,则首先需要实现一个结点类。而一个结点需要存储的信息有:数据、前一个结点的地址、后一个结点的地址(跟我们之前实现的C语言版本差不多,只不过在C语言叫结构体,而且它没有自动初始化的功能,而C++有构造函数可以解决这一问题)

而对于该结点类的成员函数来说,我们只需实现一个构造函数即可,它的节点释放可用list这个大类来实现

list的节点类

// List的节点类
	template <class T>
	 struct ListNode
	{
    
    
		ListNode(const T& val=T())
			:_val(val)
			, _next(nullptr)
			, _prev(nullptr)
		{
    
    }

		T _val;
		ListNode* _next;
		ListNode* _prev;
	};

2.2 迭代器的模拟实现

迭代器就是把不同的数据结构 "相同功能 "的函数装到一个名字相同的函数里,这样的话你在写算法的时候就可以不管你要操作的数据结构的逻辑结构了。
比如不管是链表,数组还是别的什么,统一都用迭代器进行访问的话可能都是 Next()表示下一个元素 Pre()表示上一个元素等等

至于迭代器怎么能够做到这样的功能呢,首先就是我们要实现这样的功能,必须要对对应的数据结构有一定的理解,比如vector,

我们要实现++就是下一个元素,–就是上一个元素,对于vector来说这是非常简单的,因为vector的定义是什么呀,它就是在一段

连续的空间中存储数据,因为地址的连续原生的指针就天然的就形成了所谓的迭代器了

image-20230109153522791

那么对于list呢?

很不幸,list的结构导致它的迭代器并没有vector的迭代器这么简单,因为list的空间并不连续,它的节点都是按需申请的,是用一个指针把它串联起来,这就导致了我们仅仅对它的原生指针进行++,–这样后操作后,得到的结果并不一定是我们想象的下一个节点。

所以我们需要对它的迭代器进行重新封装,并不能认为它的原生指针就可以当迭代器!!!

image-20230109153652762

​ 那么我们怎么实现这个迭代器呢?

其实我们熟悉这个结构,就可以很容易知道,我们要实现++找到下一个元素,其实对于list的操作就是它节点的next指针对应的地址就是它下一个元素,–找到上一个元素,就是prev指针对应的地址,所以我们只需要对++,–等操作符进行操作符重载即可!

/*
	 List 的迭代器
	 迭代器有两种实现方式,具体应根据容器底层数据结构实现:
	 1. 原生态指针,比如:vector
	 2. 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:
	 1. 指针可以解引用,迭代器的类中必须重载operator*()
	 2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
	 3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
	 至于operator--()/operator--(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前移动,所以需要重载
	 4. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
	 */
	 template <class T,class Ref, class Ptr>
	 class ListIterator
	 {
    
    
	 public:
		 typedef ListNode<T> Node;
		 typedef ListIterator<T, Ref, Ptr> Self;//Self是当前迭代器对象的类型:
	 public:
		 // Ref 和 Ptr 类型需要重定义下,实现反向迭代器时需要用到
		 typedef Ref Ref;
		 typedef Ptr Ptr;
		 ListIterator(Node* node=nullptr)
			 :_node(node)
		 {
    
    }
		 //
		 // 具有指针类似行为
         //返回当前结点指针所指结点的数据
		 Ref operator*()
		 {
    
    
			 return _node->_val;
		 }
		//对于->运算符的重载,我们直接返回结点当中所存储数据的地址即可
		 Ptr operator->()
		 {
    
    
			 return &(_node->_val);
		 }
		 //
		 // 迭代器支持移动
         //前置++:先让结点指针指向后一个结点,然后再返回“自增”后的结点指针
		 Self operator++()
		 {
    
    
			 _node = _node->_next;
			 return *this;
		 }
		//后置++:先记录当前结点指针的指向,然后让结点指针指向后一个结点,最后返回“自增”前的结点指针
		 Self operator++(int)
		 {
    
    
			 Self tem(*this);
			 _node = _node->_next;
			 return tem;
		 }
		 Self operator--()
		 {
    
    
			 _node = _node->_prev;
			 return *this;
		 }
		 Self operator--(int)
		 {
    
    
			 Self tem(*this);
			 _node = _node->_prev;
			 return tem;
		 }
		 //
		 // 迭代器支持比较
         //判断这两个迭代器当中的结点指针的指向是否不同
		 bool operator!=(const Self& lt)const
		 {
    
    
			 return _node != lt._node;
		 }
		//判断这两个迭代器当中的结点指针的指向是否相同
		 bool operator==(const Self& lt)const
		 {
    
    
			 return _node == lt._node;
		 }

		 Node* _node;
	 };

迭代器类的模板参数说明
这里我们所实现的迭代器类的模板参数列表当中为什么有三个模板参数?

template<class T, class Ref, class Ptr>

在list的模拟实现当中,我们typedef了两个迭代器类型,普通迭代器和const迭代器。

// 正向迭代器
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;

这里我们就可以看出,迭代器类的模板参数列表当中的Ref和Ptr分别代表的是引用类型和指针类型

当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。

若该迭代器类不设计三个模板参数,那么就不能很好的区分普通迭代器和const迭代器。

->运算符的重载说明

//对于->运算符的重载,我们直接返回结点当中所存储数据的地址即可
Ptr operator->()
{
    
    
    return &(_node->_val);
}

对于->运算符的重载,我们直接返回结点当中所存储数据的地址

可能你会觉得不对,按照这种重载方式的话,这里使用迭代器访问日期类当中的成员变量时不是应该用两个->吗?

image-20230109161749009


2.3 反向迭代器模拟实现

实现反向迭代器前首先要知道一个名词“适配器

所谓的适配器就是:通过限制模型的功能以让它满足另一个模型的功能,相当于改变了接口,但实现不变。

我们要实现反向迭代器其实很简单,就在原来的迭代器基础上改接口即可

image-20230109155345060

但是对于operator*()我们要特殊处理一下,因为反向迭代器的rbegin对应的是原迭代器的end()

也就是说它指向了最后一个元素的后一个位置,所以要取这个rbegin的元素的时候,我们要对end()–后再取元素

Ref operator*()
{
    
    
    iterator tmp = _it;
    return *(--tmp);
}

反向迭代器的适配器实现

//适配器
// 给我不同容器的正向迭代器,适配出对应的这个容器需要的反向迭代器
template <class iterator>
    class ReverseIterator
    {
    
    
        // 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的一个类型,而不是静态成员变量
        // 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量
        // 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
        public:
        typedef typename iterator::Ref Ref;
        typedef typename iterator::Ptr Ptr;
        typedef ReverseIterator<iterator> Self;

        ReverseIterator(iterator it)
            :_it(it)
            {
    
    }
        //前置++
        Self operator++()
        {
    
    
            --_it;
            return *this;
        }
        //后置++
        Self operator++(int)
        {
    
    
            Self tmp(*this);
            --_it;
            return tmp;
        }
        //前置--
        Self operator--()
        {
    
    
            ++_it;
            return *this;
        }
        //后置--
        Self operator--(int)
        {
    
    
            Self tmp(*this);
            ++_it;
            return tmp;
        }

        Ref operator*()
        {
    
    
            iterator tmp = _it;
            return *(--tmp);
        }

        Ptr operator->()
        {
    
    
            return &(operator*());
        }

        bool operator!=(const Self & l) const
        {
    
    
            return _it != l._it;
        }
        private:
        iterator _it;
    };

2.4 list的模拟实现

构造函数

list是一个带头双向循环链表,在构造一个list对象时,直接申请一个头结点,并让其前驱指针和后继指针都指向自己即可

image-20230109162251228

无参的简单构造

list()
{
    
    
    CreatNode();
}
void CreatNode()
{
    
    
    _head = new Node;
    _head->_next = _head;
    _head->_prev = _head;
}

迭代器构造

这里的迭代器不一定是list的迭代器,也可能是用的其他容器来构造list,所以需要模板实现

//迭代器构造
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
    
    
    CreatNode();
    while (first != last)
    {
    
    
        push_back(*first);
        ++first;
    }
}

拷贝构造

拷贝构造函数就是根据所给list容器,拷贝构造出一个对象

对于拷贝构造,这里提供两种写法:

  1. 传统写法

    先申请一个头结点,并让其前驱指针和后继指针都指向自己,然后将所给容器当中的数据,通过遍历的方式一个个尾插到新构造的容器后面

//拷贝构造函数
list(const list<T>& lt)
{
    
    
	_head = new node; //申请一个头结点
	_head->_next = _head; //头结点的后继指针指向自己
	_head->_prev = _head; //头结点的前驱指针指向自己
	for (const auto& e : lt)
	{
    
    
		push_back(e); //将容器lt当中的数据一个个尾插到新构造的容器后面
	}
}
  1. 现代写法

    利用迭代器构造函数,构造一个一模一样的tmp,然后再跟它交换

//拷贝构造
list(const list<T>& l)
{
    
    
    CreatNode();
    list<T> tmp(l.begin(), l.end());
    swap(tmp);
}

赋值运算符重载函数

对于赋值运算符的重载,这里也提供两种写法:

  1. 传统写法
    这是比较容易理解的一种写法,先调用clear函数将原容器清空,然后将容器lt当中的数据,通过遍历的方式一个个尾插到清空后的容器当中
//传统写法
list<T>& operator=(const list<T>& lt)
{
    
    
	if (this != &lt) //避免自己给自己赋值
	{
    
    
		clear(); //清空容器
		for (const auto& e : lt)
		{
    
    
			push_back(e); //将容器lt当中的数据一个个尾插到链表后面
		}
	}
	return *this; //支持连续赋值
}
  1. 现代写法

    首先利用编译器机制,故意不使用引用接收参数,通过编译器自动调用list的拷贝构造函数构造出来一个list对象,然后调用swap函数将原容器与该list对象进行交换

//现代写法
list<T>& operator=(list<T> tmp)
{
    
    
    swap(tmp);	
    return *this;
}

析构函数

先调用clear函数清理容器当中的数据,然后将头结点释放,最后将头指针置空

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

迭代器相关函数

begin:返回的是第一个有效数据的迭代器

end:最后一个有效数据的下一个位置的迭代器

对于list这个带头双向循环链表来说,第一个有效数据的迭代器就是头结点后的第一个结点的地址构造出来的迭代器,而其最后一个有效数据的下一个位置的迭代器就是头结点的地址构造出来的迭代器

// List的迭代器
iterator begin()
{
    
    
    return iterator(_head->_next);
}
iterator end()
{
    
    
    return iterator(_head);
}

//const迭代器也必不可少
const_iterator begin() const 
{
    
    
    return const_iterator(_head->_next);
}

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

对于反向迭代器

rbegin:就是正向迭代器的end()

rend: 就是正向迭代器的begin()

reverse_iterator rbegin()
{
    
    
    return reverse_iterator(end());
}


reverse_iterator rend()
{
    
    
    return reverse_iterator(begin());
}

//const迭代器
const_reverse_iterator rbegin() const 
{
    
    
    return reverse_iterator(end());
}


const_reverse_iterator rend() const 
{
    
    
    return reverse_iterator(begin());
}

插入、删除函数

插入函数算是老生常谈了,跟以往不同的就是这次要返回新节点位置的迭代器

在这里插入图片描述

// 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& x)
{
    
    
    Node* newNode = new Node(x);
    Node* cur = pos._node;
    //将新节点插入
    cur->_prev->_next = newNode;
    newNode->_prev = cur->_prev;
    cur->_prev = newNode;
    newNode->_next = cur;

    return	iterator(newNode);
}

还有一些头插,尾插的接口,其实都可以复用上面的插入函数

void push_back(const T& val)
{
    
    
    insert(iterator(_head), val);
}

void push_front(const T&val)
{
    
    
    insert(iterator(_head->_next), val);
}

erase函数可以删除所给迭代器位置的结点。同时还要返回该位置下一个节点的迭代器,可以有效防止迭代器失效的问题。

// 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
{
    
    
    Node* del = pos._node;
    Node* Ret = del->_next;

    //删除节点
    del->_prev->_next = Ret;
    Ret->_prev = del->_prev;
    delete del;
    return iterator(Ret);
}

当然还有一些头删,尾删,也是可以复用上面的接口

void pop_back()
{
    
    
    erase(_head->_prev);
}

void pop_front()
{
    
    
    erase(_head->_next);
}

resize

resize函数的规则:

  1. 若当前容器的size小于所给n,则尾插结点,直到size等于n为止。
  2. 若当前容器的size大于所给n,则只保留前n个有效数据。
void resize(size_t newsize, const T& val = T())
{
    
    
	size_t oldsize = size();
	//newsize<oldsize,将元素减少到newsize
	if (newsize <= oldsize)
	{
    
    
		while (newsize < oldsize)
		{
    
    
			pop_back();
			--oldsize;
		}
	}
	else //newsize>oldsize,用val扩充元素到newsize
	{
    
    
		while (newsize > oldsize)
		{
    
    
			push_back(val);
			oldsize++;
		}
	}
}

clear

clear函数用于清空容器,我们通过遍历的方式,逐个删除结点,只保留头结点

void clear()
{
    
    
	Node*cur = _head->_next;
	while (cur != _head)
	{
    
    
		Node* del = cur;
		cur = cur->_next;
		delete del;
	}
}

swap

swap函数用于交换两个容器,list容器当中存储的实际上就只有链表的头指针,我们将这两个容器当中的头指针交换

void swap(list<T>& l)
{
    
    
	std::swap(_head, l._head);
}

这里我们复用标准库里面的交换函数swap即可,不过要主要的是要加上访问限定符,没加std::就有可以导致无限递归


完整的实现代码

#pragma once 
#include <iostream>
using namespace std;

namespace hdm
{
    
    
	// List的节点类
	template <class T>
	 struct ListNode
	{
    
    
		ListNode(const T& val=T())
			:_val(val)
			, _next(nullptr)
			, _prev(nullptr)
		{
    
    }

		T _val;
		ListNode* _next;
		ListNode* _prev;
	};
	 /*
	 List 的迭代器
	 迭代器有两种实现方式,具体应根据容器底层数据结构实现:
	 1. 原生态指针,比如:vector
	 2. 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:
	 1. 指针可以解引用,迭代器的类中必须重载operator*()
	 2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
	 3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
	 至于operator--()/operator--(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前移动,所以需要重载,如果是forward_list就不需要重载--
	 4. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
	 */

	 template <class T,class Ref, class Ptr>
	 class ListIterator
	 {
    
    
	 public:
		 typedef ListNode<T> Node;
		 typedef ListIterator<T, Ref, Ptr> Self;
	 public:
		 // Ref 和 Ptr 类型需要重定义下,实现反向迭代器时需要用到
		 typedef Ref Ref;
		 typedef Ptr Ptr;
		 ListIterator(Node* node=nullptr)
			 :_node(node)
		 {
    
    }
		 //
		 // 具有指针类似行为
		 Ref operator*()
		 {
    
    
			 return _node->_val;
		 }

		 Ptr operator->()
		 {
    
    
			 return &(_node->_val);
		 }
		 //
		 // 迭代器支持移动
		 Self operator++()
		 {
    
    
			 _node = _node->_next;
			 return *this;
		 }

		 Self operator++(int)
		 {
    
    
			 Self tem(*this);
			 _node = _node->_next;
			 return tem;
		 }
		 Self operator--()
		 {
    
    
			 _node = _node->_prev;
			 return *this;
		 }
		 Self operator--(int)
		 {
    
    
			 Self tem(*this);
			 _node = _node->_prev;
			 return tem;
		 }

		 //
		 // 迭代器支持比较
		 bool operator!=(const Self& lt)const
		 {
    
    
			 return _node != lt._node;
		 }

		 bool operator==(const Self& lt)const
		 {
    
    
			 return _node == lt._node;
		 }



		 Node* _node;
	 };

	 //适配器
	 // 给我不同容器的正向迭代器,适配出对应的这个容器需要的反向迭代器
	 template <class iterator>
	 class ReverseIterator
	 {
    
    
		 // 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的一个类型,而不是静态成员变量
		 // 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量
		 // 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
	 public:
		 typedef typename iterator::Ref Ref;
		 typedef typename iterator::Ptr Ptr;
		 typedef ReverseIterator<iterator> Self;

		 ReverseIterator(iterator it)
			 :_it(it)
		 {
    
    }

		 Self operator++()
		 {
    
    
			 --_it;
			 return *this;
		 }

		 Self operator--()
		 {
    
    
			 ++_it;
			 return *this;
		 }

		 Ref operator*()
		 {
    
    
			 iterator tmp = _it;
			 return *(--tmp);
		 }
		 
		 Ptr operator->()
		 {
    
    
			 return &(operator*());
		 }

		 bool operator!=(const Self & l) const
		 {
    
    
			 return _it != l._it;
		 }
	 private:
		 iterator _it;
	 };



	template <class T>
	class list
	{
    
    
	public:
		typedef ListNode<T> Node;
		// 正向迭代器
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

		// 反向迭代器
		typedef ReverseIterator<iterator> reverse_iterator;
		typedef ReverseIterator<const_iterator> const_reverse_iterator;

		list()
		{
    
    
			CreatNode();
		}

		list(int n, const T& val = T())
		{
    
    
			CreatNode();
			while (n--)
			{
    
    
				push_back(val);
			}
		}

		//迭代器构造
		template<class InputIterator>
		list(InputIterator first, InputIterator last)
		{
    
    
			CreatNode();
			while (first != last)
			{
    
    
				push_back(*first);
				++first;
			}
		}

		//拷贝构造
		list(const list<T>& l)
		{
    
    
			CreatNode();
			list<T> tmp(l.begin(), l.end());
			swap(tmp);
		}

		list<T>& operator=(list<T> tmp)
		{
    
    
			swap(tmp);	
			return *this;
		}


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


		///
		// List的迭代器
		iterator begin()
		{
    
    
			return iterator(_head->_next);
		}
		iterator end()
		{
    
    
			return iterator(_head);
		}

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

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

		reverse_iterator rbegin()
		{
    
    
			return reverse_iterator(end());
		}

		const_reverse_iterator rbegin() const 
		{
    
    
			return reverse_iterator(end());
		}

		reverse_iterator rend()
		{
    
    
			return reverse_iterator(begin());
		}

		const_reverse_iterator rend() const 
		{
    
    
			return reverse_iterator(begin());
		}
		///
		// List的容量相关

		size_t size() const
		{
    
    
			size_t count = 0;
			Node*cur = _head->_next;
			while (cur != _head)
			{
    
    
				count++;
				cur = cur->_next;
			}
			return count;
		}

		bool empty()const
		{
    
    
			return _head->_next == _head;	
		}

		void resize(size_t newsize, const T& val = T())
		{
    
    
			size_t oldsize = size();
			//newsize<oldsize,将元素减少到newsize
			if (newsize <= oldsize)
			{
    
    
				while (newsize < oldsize)
				{
    
    
					pop_back();
					--oldsize;
				}
			}
			else
			{
    
    
				while (newsize > oldsize)
				{
    
    
					push_back(val);
					oldsize++;
				}
			}
		}

		
		// List的元素访问操作
		// 注意:List不支持operator[]

		T& front()
		{
    
    
			return _head->_next -> val;
		}
		const T& front()const 
		{
    
    
			return _head->_next->val;
		}

		T& back()
		{
    
    
			return _head->_prev->_val;
		}
		const T& back() const 
		{
    
    
			return _head->_prev->_val;
		}

		
		// List的插入和删除
		
		// 在pos位置前插入值为val的节点
		iterator insert(iterator pos, const T& x)
		{
    
    
			Node* newNode = new Node(x);
			Node* cur = pos._node;
			//将新节点插入
			cur->_prev->_next = newNode;
			newNode->_prev = cur->_prev;
			cur->_prev = newNode;
			newNode->_next = cur;

			return	iterator(newNode);
		}

		void push_back(const T& val)
		{
    
    
			insert(iterator(_head), val);
		}

		void push_front(const T&val)
		{
    
    
			insert(iterator(_head->_next), val);
		}

		// 删除pos位置的节点,返回该节点的下一个位置
		iterator erase(iterator pos)
		{
    
    
			Node* del = pos._node;
			Node* Ret = del->_next;

			//删除节点
			del->_prev->_next = Ret;
			Ret->_prev = del->_prev;
			delete del;
			return iterator(Ret);
		}

		void pop_back()
		{
    
    
			erase(_head->_prev);
		}

		void pop_front()
		{
    
    
			erase(_head->_next);
		}

		void clear()
		{
    
    
			Node*cur = _head->_next;
			while (cur != _head)
			{
    
    
				Node* del = cur;
				cur = cur->_next;
				delete del;
			}
		}

		void swap(list<T>& l)
		{
    
    
			std::swap(_head, l._head);
		}
	private:
		void CreatNode()
		{
    
    
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
	private:
		Node* _head;
	};


	///
	// 对模拟实现的list进行测试
	// 正向打印链表
	template<class T>
	void PrintList(const hdm::list<T>& l)
	{
    
    
		auto it = l.begin();
		while (it != l.end())
		{
    
    
			cout << *it << " ";
			++it;
		}

		cout << endl;
	}

	// 测试List的构造
	void TesthdmList1()
	{
    
    
		hdm::list<int> l1;
		hdm::list<int> l2(10, 5);
		PrintList(l2);

		int array[] = {
    
     1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
		hdm::list<int> l3(array, array + sizeof(array) / sizeof(array[0]));
		PrintList(l3);

		hdm::list<int> l4(l3);
		PrintList(l4);

		l1 = l4;
		PrintList(l1);
	}

	// PushBack()/PopBack()/PushFront()/PopFront()
	void TesthdmList2()
	{
    
    
		// 测试PushBack与PopBack
		hdm::list<int> l;
		l.push_back(1);
		l.push_back(2);
		l.push_back(3);
		PrintList(l);

		l.pop_back();
		l.pop_back();
		PrintList(l);

		l.pop_back();
		cout << l.size() << endl;

		// 测试PushFront与PopFront
		l.push_front(1);
		l.push_front(2);
		l.push_front(3);
		PrintList(l);

		l.pop_front();
		l.pop_front();
		PrintList(l);

		l.pop_front();
		cout << l.size() << endl;
	}

	// 测试insert和erase
	void TesthdmList3()
	{
    
    
		int array[] = {
    
     1, 2, 3, 4, 5 };
		hdm::list<int> l(array, array + sizeof(array) / sizeof(array[0]));

		auto pos = l.begin();
		l.insert(l.begin(), 0);
		PrintList(l);

		++pos;
		l.insert(pos, 2);
		PrintList(l);

		l.erase(l.begin());
		l.erase(pos);
		PrintList(l);

		// pos指向的节点已经被删除,pos迭代器失效
		cout << *pos << endl;

		auto it = l.begin();
		while (it != l.end())
		{
    
    
			it = l.erase(it);
		}
		cout << l.size() << endl;
	}

	// 测试反向迭代器
	void TesthdmList4()
	{
    
    
		int array[] = {
    
     1, 2, 3, 4, 5 };
		hdm::list<int> l(array, array + sizeof(array) / sizeof(array[0]));

		auto rit = l.rbegin();
		while (rit != l.rend())
		{
    
    
			cout << *rit << " ";
			++rit;
		}
		cout << endl;

		const hdm::list<int> cl(l);
		auto crit = l.rbegin();
		while (crit != l.rend())
		{
    
    
			cout << *crit << " ";
			++crit;
		}
		cout << endl;
	}
}

猜你喜欢

转载自blog.csdn.net/dongming8886/article/details/128620366