list模拟实现【C++】

07-12 1304阅读

文章目录

  • 全部的实现代码放在了文章末尾
  • 准备工作
    • 包含头文件
    • 定义命名空间
    • 类的成员变量
      • 为什么节点类是用struct而不是class呢?
      • 为什么要写get_head_node?
      • 迭代器
        • 迭代器在list类里的实例化和重命名
        • 普通迭代器
          • operator->()的作用是什么?
          • const迭代器
          • 反向迭代器
          • 迭代器的获取
          • 构造函数
            • 默认构造
            • 使用n个val构造
            • 迭代器区间构造
            • 解决迭代器区间构造 和 用n个val构造的冲突
            • initializer_list构造
            • 拷贝构造
            • 析构函数
            • swap
            • 赋值运算符重载
            • erase
              • 删除pos迭代器指向的节点
                • 为什么要返回next?
                • 删除迭代器区间
                • insert
                  • 在迭代器pos之前插入一个节点
                  • 为什么要返回newnode?
                  • 在迭代器pos之前插入一个迭代器区间的数据
                  • push_back
                  • push_front
                  • pop_front
                  • pop_back
                  • size
                  • empty
                  • back
                  • front
                  • assign
                  • resize
                  • clear
                  • 全部代码

                    全部的实现代码放在了文章末尾

                    准备工作

                    创建两个文件,一个头文件mylist.hpp,一个源文件test.cpp

                    【因为模板的声明和定义不能分处于不同的文件中,所以把成员函数的声明和定义放在了同一个文件mylist.hpp中】

                    mylist.hpp:存放包含的头文件,命名空间的定义,成员函数和命名空间中的函数的定义

                    test.cpp:存放main函数,以及测试代码


                    包含头文件

                    1. iostream:用于输入输出

                    2. assert.h:用于使用报错函数assert


                    定义命名空间

                    在文件mylist.hpp中定义上一个命名空间mylist

                    把list类和它的成员函数放进命名空间封装起来,防止与包含的头文件中的函数/变量重名的冲突问题


                    类的成员变量

                    参考了stl源码中的list的实现,stl中list的底层链表是双向带头循环链表

                    【可以看我这篇文章了解双向带头循环链表的实现:链表的极致——带头双向循环链表】

                    成员变量只有一个,就是指向双向带头循环链表的头节点的指针。

                    节点类:

                    list模拟实现【C++】

                    为什么节点类是用struct而不是class呢?

                    因为节点类里面的成员变量在实现list的时候需要经常访问,所以需要节点类的成员变量是公有的【使用友元也可以,但是比较麻烦】

                    struct的默认访问权限就是公有,不用加访问限定符了,stl中实现的节点类也是struct

                    class的默认访问权限是私有的


                    list类:

                    list模拟实现【C++】

                    为什么要写get_head_node?

                    因为插入节点之前必须要有头节点

                    所以把创建初始头节点的操作写成了一个函数,用于

                    所有构造函数插入节点之前进行申请头节点


                    迭代器

                    因为list存储数据的方式是创建一个一个的节点存储数据

                    所以存储数据的空间不是连续的,所以不能直接用指针作为迭代器

                    因为指向一个节点的指针直接++,是不一定能指向下一个节点的

                    所以要把迭代器实现成一个类,这样才可以正确地支持++,- -,*等操作

                    迭代器在list类里的实例化和重命名

                    list模拟实现【C++】

                    普通迭代器

                    template
                    struct Iterator
                    {
                    	把自己的类型重命名一下
                    	typedef Iterator Self;
                    	成员变量的类型是 双向带头循环链表的节点类型
                    	listnode* _n;
                    	Iterator(listnode*l=nullptr) 构造函数
                    	{
                    		_n = l;
                    	}
                    	Self& operator++()前置++
                    	{
                    		++就是指向下一个节点
                    		_n = _n->_next;
                    		return *this;
                    	}
                    	Self operator++(int)  后置++
                    	{
                    		Self tmp =*this;  先记录一下++之前的值
                    		_n = _n->_next;  再++
                    		return tmp;
                    	}
                    	Self& operator--()  前置--
                    	{
                    		--就是指向上一个节点
                    		_n = _n->_prev;
                    		return *this;
                    	}
                    	Self operator--(int) 后置--
                    	{
                    		Self tmp = *this; 先记录一下--之前的值
                    		_n = _n->_prev;  再--
                    		return tmp;
                    	}
                    	R operator*()const
                    	{
                    		 类比指针
                    		*就是获取  节点中存储的数据
                    		return _n->_data;
                    	}
                    	F operator->()const
                    	{
                    		返回  节点中 存储数据的成员变量的  地址
                    		return &(_n->_data);
                    	}
                    	bool operator!=(const Self&obj)const![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/f3d8c3b768b14a04a69d04a69c2d9829.png#pic_center)
                    	{
                    		return _n != obj._n;
                    	}
                    	bool operator==(const Self& obj)
                    	{
                    		return !(*this != obj);
                    	}
                    };
                    

                    operator->()的作用是什么?

                    为了实现:

                    当节点中存储的数据是自定义类型的变量时,可以直接使用 迭代器->访问自定义类型中的成员

                    【原来访问需要调用两次->,为了可读性,省略了一个->】

                    list模拟实现【C++】


                    const迭代器

                    const迭代器与普通迭代器的区别是什么?

                    区别就只有==不能通过const迭代器改变节点中存储的数据==

                    转换一下就是:

                    1. 不能使用迭代器的operator*()改变节点中存储的数据,即把operator*()的返回值改成const T&就可以了
                    2. 不能使用迭代器的operator->()改变节点中存储的数据的成员,即把operator->()的返回值改成const T*就可以了

                    所以const迭代器与普通迭代器的区别就只有两个函数的返回值类型不同,所以增加两个模板参数:R和F

                    普通迭代器实例化时:R就是T&,F就是T*

                    const迭代器实例化:R就是const T&,F就是const T*


                    反向迭代器

                    反向迭代器与普通迭代器的实现上的区别就是:

                    普通迭代器++是指向下一个节点

                    反向迭代器++是指向上一个节点

                    普通迭代器- -是指向上一个节点

                    反向迭代器- -是指向下一个节点

                    template
                    struct Reverse_iterator
                    {
                    	把自己的类型重命名一下
                    	typedef Reverse_iterator Self;
                    	成员变量的类型是 双向带头循环链表的节点类型
                    	listnode* _n;
                    	Reverse_iterator(listnode* l) 构造函数
                    	{
                    		_n = l;
                    	}
                    	Self& operator++()
                    	{
                    		反向迭代器++,是移动到  前  一个节点
                    		_n = _n->_prev;
                    		return *this;
                    	}
                    	Self operator++(int)
                    	{
                    		Self tmp = *this;
                    		_n = _n->_prev;
                    		return tmp;
                    	}
                    	Self& operator--()
                    	{
                    		反向迭代器--,是移动到  后  一个节点
                    		_n = _n->_next;
                    		return *this;
                    	}
                    	Self operator--(int)
                    	{
                    		Self tmp = *this;
                    		_n = _n->_next;
                    		return tmp;
                    	}
                    	R operator*()const
                    	{
                    		return _n->_data;
                    	}
                    	F operator->()const
                    	{
                    		return &(_n->_data) ;
                    	}
                    	bool operator!=(const Self& obj)const
                    	{
                    		return _n!=obj._n;
                    	}
                    	bool operator==(const Self& obj)const
                    	{
                    		return !(*this != obj);
                    	}
                    };
                    

                    迭代器的获取

                    iterator begin()
                    {
                    	头节点的下一个节点 才是第一个节点
                    	return _head->_next;
                    }
                    const修饰的对象只能调用const修饰的成员函数
                    const_iterator begin()const
                    {
                    	头节点的下一个节点 才是第一个节点
                    	return _head->_next;
                    }
                    iterator end()
                    {
                    	最后一个节点的下一个节点是  头节点
                    	因为是循环链表
                    	return _head;
                    }
                    const修饰的对象只能调用const修饰的成员函数
                    const_iterator end()const
                    {
                    	return _head;
                    }
                    reverse_iterator rend()
                    {
                    	反向迭代器的rend返回的是第一个节点的   前一个节点
                    	return _head;
                    }
                    const修饰的对象只能调用const修饰的成员函数
                    const_reverse_iterator rend()const
                    {
                    	return _head;
                    }
                    reverse_iterator rbegin()
                    {
                    	反向迭代器的rbegin()返回的是  最后一个节点
                    	最后一个节点是  头节点的前一个
                    	因为是循环链表
                    	return _head->_prev;
                    }
                    const_reverse_iterator rbegin()const
                    {
                    	return _head->_prev;
                    }
                    

                    构造函数

                    默认构造

                    list模拟实现【C++】


                    使用n个val构造

                    list模拟实现【C++】


                    迭代器区间构造

                    list模拟实现【C++】


                    解决迭代器区间构造 和 用n个val构造的冲突

                    当重载了迭代器区间构造和使用n个val构造的时候

                    如果传入的两个参数都是int类型的话就会报错

                    为什么?

                    因为在模板函数构成重载时,编译器会调用更合适的那一个

                    什么叫更合适?

                    就是不会类型转

                    如果传入的两个参数都是int类型,那么调用的应该是使用n个值构造,因为没有int类型的迭代器

                    但是使用n个值构造的第一个参数是size_t , int传进去要隐式类型转换

                    而调用迭代器区间构造,两个int的实参传进去,就会直接把InputIterator推导成int,不会发生类型转换,所以编译器会调用迭代器区间构造

                    解决方法:

                    再重载一个使用n个值构造的函数,把第一个参数改成int,这样根据模板偏特化,就会在都不类型转换时优先调用第一个参数特化成int的那个构造函数

                    list模拟实现【C++】


                    initializer_list构造

                    写了这个构造函数,就可以支持直接使用{}初始化了

                    list模拟实现【C++】

                    initializer_list是iostream库里面的自定义类型,它可以直接接收{ }里面的值 进行初始化,而且有迭代器

                    所以可以直接使用迭代器循环+尾插进行对list的构造

                    list模拟实现【C++】


                    拷贝构造

                    list模拟实现【C++】


                    析构函数

                    list模拟实现【C++】


                    swap

                    只需要交两个list对象的头指针中存储的地址就可以了

                    因为两个list对象都有头结点,交换了头指针中存储的地址,就相当于把这两个对象的头指针的指向交换了,

                    而链表的所有节点都是由头节点出发去找到的

                    list模拟实现【C++】

                    void swap(list& obj)
                    {
                    	调用库里面的swap,交换头指针
                    	std::swap(_head, obj._head);
                    }
                    

                    赋值运算符重载

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

                    为什么上面的两句代码就可以完成深拷贝呢?

                    这是因为:

                    使用了传值传参,会在传参之前调用拷贝构造,再把拷贝构造出的临时对象作为参数传递进去

                    赋值运算符的左操作数,*this再与传入的临时对象obj交换,就直接完成了拷贝

                    在函数结束之后,存储在栈区的obj再函数结束之后,obj生命周期结束

                    obj调用析构函数,把指向的从*this那里交换来的不需要的空间销毁


                    erase

                    删除pos迭代器指向的节点

                    list模拟实现【C++】

                    list模拟实现【C++】

                    为什么要返回next?

                    因为使用了erase之后的迭代器会失效,需要提供更新的方法

                    为什么使用了erase之后的迭代器会失效?

                    因为pos指向的节点erase之后,节点被释放了

                    stl库里面规定erase的返回值是指向删除数据的下一个数据的迭代器,下一个数据就是next指向的数据,所以返回next【没有接收返回值的迭代器,在检测较严格的编译器中,不管指向的位置是否正确,都会禁止使用,使用了就报错】


                    删除迭代器区间

                    list模拟实现【C++】


                    insert

                    在迭代器pos之前插入一个节点

                    list模拟实现【C++】

                    list模拟实现【C++】

                    为什么要返回newnode?

                    list的迭代器pos在使用完insert之后其实是不会失效的

                    但是为了与其他容器的nsert的返回值进行统一,所以也返回了指向新插入的节点的迭代器


                    在迭代器pos之前插入一个迭代器区间的数据

                    list模拟实现【C++】


                    push_back

                    复用insert即可

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

                    push_front

                    复用insert即可

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

                    pop_front

                    复用erese即可

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

                    pop_back

                    复用erese即可

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

                    size

                    list模拟实现【C++】


                    empty

                    list模拟实现【C++】


                    back

                    list模拟实现【C++】


                    front

                    list模拟实现【C++】


                    assign

                    list模拟实现【C++】


                    resize

                    list模拟实现【C++】


                    clear

                    复用erase

                    list模拟实现【C++】


                    全部代码

                    #include
                    #include
                    using namespace std;
                    namespace mylist
                    {
                    	template
                    	struct listnode//双向带头循环链表的节点类
                    	{
                    		T _data;//节点存储的数据
                    		listnode* _next;//指向下一个节点的指针
                    		listnode* _prev;//指向前一个节点的指针
                    	};
                    	
                    	
                    	template
                    	struct Iterator
                    	{
                    		//把自己的类型重命名一下
                    		typedef Iterator Self;
                    		//成员变量的类型是 双向带头循环链表的节点类型
                    		listnode* _n;
                    		Iterator(listnode*l=nullptr)//构造函数
                    		{
                    			_n = l;
                    		}
                    		Self& operator++()//前置++
                    		{
                    			//++就是指向下一个节点
                    			_n = _n->_next;
                    			return *this;
                    		}
                    		Self operator++(int)//后置++
                    		{
                    			Self tmp =*this;//先记录一下++之前的值
                    			_n = _n->_next;//再++
                    			return tmp;
                    		}
                    		Self& operator--()//前置--
                    		{
                    			//--就是指向上一个节点
                    			_n = _n->_prev;
                    			return *this;
                    		}
                    		Self operator--(int)//后置--
                    		{
                    			Self tmp = *this;//先记录一下--之前的值
                    			_n = _n->_prev;//再--
                    			return tmp;
                    		}
                    		R operator*()const
                    		{
                    			//类比指针
                    			//*就是获取  节点中存储的数据
                    			return _n->_data;
                    		}
                    		F operator->()const
                    		{
                    			//返回  节点中 存储数据的成员变量的  地址
                    			return &(_n->_data);
                    		}
                    		bool operator!=(const Self&obj)const
                    		{
                    			return _n != obj._n;
                    		}
                    		bool operator==(const Self& obj)
                    		{
                    			return !(*this != obj);
                    		}
                    	};
                    	template
                    	struct Reverse_iterator
                    	{
                    		//把自己的类型重命名一下
                    		typedef Reverse_iterator Self;
                    		//成员变量的类型是 双向带头循环链表的节点类型
                    		listnode* _n;
                    		Reverse_iterator(listnode* l)//构造函数
                    		{
                    			_n = l;
                    		}
                    		Self& operator++()
                    		{
                    			//反向迭代器++,是移动到  前  一个节点
                    			_n = _n->_prev;
                    			return *this;
                    		}
                    		Self operator++(int)
                    		{
                    			Self tmp = *this;
                    			_n = _n->_prev;
                    			return tmp;
                    		}
                    		Self& operator--()
                    		{
                    			//反向迭代器--,是移动到  后  一个节点
                    			_n = _n->_next;
                    			return *this;
                    		}
                    		Self operator--(int)
                    		{
                    			Self tmp = *this;
                    			_n = _n->_next;
                    			return tmp;
                    		}
                    		R operator*()const
                    		{
                    			return _n->_data;
                    		}
                    		F operator->()const
                    		{
                    			return &(_n->_data) ;
                    		}
                    		bool operator!=(const Self& obj)const
                    		{
                    			return _n!=obj._n;
                    		}
                    		bool operator==(const Self& obj)const
                    		{
                    			return !(*this != obj);
                    		}
                    	};
                    	template
                    	class list
                    	{
                    		//把双向带头循环链表的节点类型重命名成node
                    		typedef listnode node;
                    	private:
                    		node* _head;//唯一的成员变量
                    		//获取初始头结点
                    		node* get_head_node()
                    		{
                    			//申请一个节点大小的空间
                    			node* tmp = new node;
                    			//最开始的头节点的prev和next都指向自己
                    			tmp->_next = tmp;
                    			tmp->_prev = tmp;
                    			return tmp;
                    		}
                    	public:
                    		
                    		typedef  Iterator iterator;//普通迭代器
                    		typedef  Iterator  const_iterator;//const迭代器
                    		typedef  Reverse_iterator  reverse_iterator;//反向迭代器
                    		typedef  Reverse_iterator const_reverse_iterator;//const反向迭代器
                    		list()
                    		{
                    			//获取头结点
                    			//为之后的插入操作做准备
                    			_head=get_head_node();
                    		}
                    		list(size_t n, const T& val=T())
                    		{
                    			//必须先获取头节点,才能进行插入数据
                    			_head = get_head_node();
                    			for (int i = 0; i _next;
                    		}
                    		//const修饰的对象只能调用const修饰的成员函数
                    		const_iterator begin()const
                    		{
                    			//头节点的下一个节点 才是第一个节点
                    			return _head->_next;
                    		}
                    		iterator end()
                    		{
                    			//最后一个节点的下一个节点是  头节点
                    			//因为是循环链表
                    			return _head;
                    		}
                    		//const修饰的对象只能调用const修饰的成员函数
                    		const_iterator end()const
                    		{
                    			return _head;
                    		}
                    		reverse_iterator rend()
                    		{
                    			//反向迭代器的rend返回的是第一个节点的   前一个节点
                    			return _head;
                    		}
                    		//const修饰的对象只能调用const修饰的成员函数
                    		const_reverse_iterator rend()const
                    		{
                    			return _head;
                    		}
                    		reverse_iterator rbegin()
                    		{
                    			//反向迭代器的rbegin()返回的是  最后一个节点
                    			//最后一个节点是  头节点的前一个
                    			//因为是循环链表
                    			return _head->_prev;
                    		}
                    		const_reverse_iterator rbegin()const
                    		{
                    			return _head->_prev;
                    		}
                    		
                    		void push_back(const T&val)
                    		{
                    			/*node* tail = _head->_prev;
                    			node* newnode = new node;
                    			newnode->_data = val;
                    			newnode->_next = _head;
                    			newnode->_prev = tail;
                    			tail->_next = newnode;
                    			_head->_prev = newnode;*/
                    			insert(end(), val);
                    		}
                    		iterator erase(iterator pos)
                    		{
                    			//不能  把 头节点 给删了
                    			assert(pos!=end());
                    			//记录pos的前一个节点(prev) 
                    			// 和后一个节点(next)
                    			node* prev = pos._n->_prev;
                    			node* next = pos._n->_next;
                    			
                    			//让prev的下一个节点变成next
                    			prev->_next = next;
                    			//让prev的上一个节点变成next
                    			next->_prev = prev;
                    			//释放pos指向的节点
                    			delete pos._n;
                    			//返回被删除的节点的下一个节点
                    			//用于更新迭代器
                    			return next;
                    		}
                    		iterator erase(iterator first, iterator last)
                    		{
                    			iterator it = first;
                    			while (it != last)
                    			{
                    				//删除it指向的节点
                    				//删除后让it接收返回值,进行更新
                    				it = erase(it);
                    			}
                    			//返回被删除的  最后一个节点  的下一个节点
                    			//用于更新迭代器
                    			return last;
                    		}
                    		bool empty() const
                    		{
                    			//size==0就   是空    返回true
                    			//size!=0就   不是空  返回false
                    			return size() ==0 ;
                    		}
                    		size_t size()const 
                    		{
                    			//用count记录节点个数
                    			size_t count = 0;
                    			//使用const迭代器接收  const修饰的对象的迭代器
                    			const_iterator it = begin();
                    			//遍历链表
                    			while (it != end())
                    			{
                    				count++;
                    				++it;
                    			}
                    			return count;
                    		}
                    		T& back()
                    		{
                    			//list不能为空,为空就报错
                    			assert(!empty());
                    			//end()返回的迭代器指向  头结点
                    			//头结点的上一个节点就是,最后一个节点
                    			//因为是循环链表
                    			return *(--end());
                    		}
                    		//const修饰的成员,只能调用const修饰的成员函数
                    		const T& back() const
                    		{
                    			assert(!empty());
                    			return *(--end());
                    		}
                    		T& front()
                    		{
                    			//list不能为空,为空就报错
                    			assert(!empty());
                    			//begin()返回的迭代器  就指向第一个节点
                    			return *begin();
                    		}
                    		//const修饰的成员,只能调用const修饰的成员函数
                    		const T& front()const
                    		{
                    			assert(!empty());
                    			return *begin();
                    		}
                    		template 
                    		void assign(InputIterator first, InputIterator last)
                    		{
                    			//先把数据现有的节点(除了头结点)都删除
                    			clear();
                    			//再循环把数据一个一个尾插进去
                    			while (first != last)
                    			{
                    				//尾插
                    				push_back(*first);
                    				first++;
                    			}
                    		}
                    		
                    		void assign(size_t n, const T& val)
                    		{
                    			clear();
                    			for (int i = 0; i _prev;
                    			//申请新节点的空间
                    			node* newnode = new node;
                    			//存储数据
                    			newnode->_data = val;
                    			newnode->_next = pos._n;
                    			newnode->_prev = prev;
                    			prev->_next = newnode;
                    			pos._n->_prev = newnode;
                    			//返回指向新插入的节点的迭代器
                    			//用于更新迭代器
                    			return newnode;
                    		}
                    		template 
                    		void insert(iterator pos, InputIterator first, InputIterator last)
                    		{
                    			while (first!=last)
                    			{
                    				//循环插入即可
                    				//因为list的迭代器使用完insert之后 不会失效
                    				//所以不用接收返回值  也可以
                    			    insert(pos,*first);
                    				first++;
                    			}
                    		}
                    		void push_front(const T& val)
                    		{
                    			insert(begin(), val);
                    		}
                    		void pop_front()
                    		{
                    			erase(begin());
                    		}
                    		void pop_back()
                    		{
                    			erase(--end());
                    		}
                    		void resize(size_t n, const T& val = T())
                    		{
                    			//获取一下size,加快后续比较效率
                    			//因为获取size()的时间复杂度为  O(N)
                    			size_t size = this->size();
                    			if (n > size)
                    			{
                                    //缺失的数据用val填上
                    				//填到size()==n为止
                    				while (size 
                                    
                                    
                                    
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]