首页 > 编程语言 >C++list类的常见函数以及其模拟实现

C++list类的常见函数以及其模拟实现

时间:2024-06-21 16:28:15浏览次数:9  
标签:node head return temp list C++ next const 模拟

文章目录


前言

C++list类的常见函数及其模拟实现

list的底层是一个双向循环链表。每个元素存储在互不相关的独立节点中,在节点中通过指针来访问前一个元素和后一个元素。与其他序列式容器相比(array,vector,deque),list可以在任意位置以O(1)的效率进行插入与删除,但其本身不可随机访问(因此不需要去重载[ ]符号)。
接下来我们将依次介绍list的模拟实现。

一、list内部成员有什么?

首先定义了一个命名空间jjw
接着在jjw内首先定义了一个双向链表的数据结构,并完成它的默认构造。
随后定义了一个Node* 类型的_head指针,指向这个链表的头结点。并且定义了一个_size表示链表结点的数量。
最后定义了一个Create_node函数,这个函数来为_head开辟一份空间,并让其_next,_pre指向其本身完成初始化。

namespace jjw
{
template<class T>
struct ListNode{
ListNode(const T& val=T())
{
	_pre = nullptr;
	_next = nullptr;
	_val = val;
}
ListNode<T>* _pre;
ListNode<T>* _next;
T _val;
};
template<class T>
class list
{
typedef ListNode<T> Node;
public:
  // 一系列函数
private:
void Create_node()
{
	_head = new Node;
	_head->_next = _head;
	_head->_pre = _head;
	_size = 0;
}
Node* _head;
size_t _size;
};
}

二、构造函数以及析构函数

1.默认构造

直接调用Create_node函数即可。

list()
{
	Create_node();
}

2.传参构造

传入两个参数,int n表示该链表的结点数目,val表示每个结点的值。
首先调用Create_node完成_head的初始化。
然后创建一个变量p用来表示每次链尾的元素。
最后通过for循坏依次插入元素,注意插入后需要将_size++。(后面我们可以直接调用push_back完成)

list(int n, const T& val = T())
{
	Create_node();
	Node* p = _head;
	for (int i = 0; i < n; i++)
	{
		Node* temp = new Node;
		temp->_val = val;
		temp->_next = p->_next;
		temp->_pre = p;
		p->_next = temp;
		_head->_pre = temp;
		p = temp;
		_size++;
		//push_back(val);
	}
}

3.迭代器构造

首先定义了一个迭代器模板,不管什么类型的迭代器都可以进行初始化。
然后就和上诉基本一样,循环遍历区间,依次在链表末尾插入元素。

template<class Iterator>
list(Iterator start,Iterator finish)
{
	Create_node();
	while (start != finish)
	{
		push_back(*start);
		++start;
	}
}

4.深拷贝以及运算符=的重载

1.深拷贝

和上述实现差不多,依次遍历lt,插入到链表的末端。

list(const list<T>& lt)
{
	Create_node();
	for (auto e : lt)
	{
		push_back(e);
	}
}

2.=的重载

首先通过lt创建了一个临时变量temp,然后将temp变量和this交换。
最后返回
this。(注意这里函数返回值类型不可以加引用)

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

5.析构函数

首先调用clear函数清除每个结点(后面会介绍clear函数)。
然后释放_head 头结点指针完成析构。

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

三、迭代器的模拟实现

1.正向迭代器

首先定义了三个模板 T表示链表中元素的类型,Ref表示T&,Ptr表示T*(重载->运算符需要)。
然后在List_Iterator内定义了Node* _node,并且定义了其默认构造。
接着对迭代器一系列的操作进行了重载,来实现正向迭代器。
在List类中,begin返回第一个元素的位置,即_head->_next。
end返回最后一个元素的下一个位置,即_head。
list不同与string和vector,它需要通过一个类来封装实现迭代器。

namespace jjw
{
template<class T,class Ref,class Ptr>
struct List_Iterator{
	typedef ListNode<T> Node;
	typedef List_Iterator<T, Ref, Ptr> self;
	typedef Ref Ref; //反向迭代器要用到
	typedef Ptr Ptr; //反向迭代器要用到
	List_Iterator(Node* node=nullptr)
	{
		_node = node;
	}

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

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

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

	self operator++(int)
	{
		self temp(_node);
		_node = _node->_next;
		return temp;
	}

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

	self operator--(int)
	{
		self temp(_node);
		_node = _node->_pre;
		return temp;
	}

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

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

	Node* _node;
};
template<class T>
class list{
public:
	typedef ListNode<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);
	}
	
	iterator end()
	{
		return _head;
	}
	
	const_iterator begin()const
	{
		return const_iterator(_head->_next);
	}
	
	const_iterator end()const
	{
		return const_iterator(_head);
    }
};
}

2.反向迭代器

反向迭代器通过封装正向迭代器来实现。
rbegin返回end()的位置,rend返回begin()的位置。
++去- -,- -去++。
注意这里的解引用操作,先 - - 再解引用。

template<class Iterator>
struct Reverse_Iterator
{
typedef Reverse_Iterator<Iterator> self;
typedef typename Iterator::Ref Ref;//typename表示取出的Ref和Ptr是Iterator中的一个类型
typedef typename Iterator::Ptr Ptr;//而不是一个静态成员变量。

Reverse_Iterator(Iterator it)
{
	_it = it;
}

Ref operator*() //返回前一个位置的元素值
{
	Iterator temp(_it);
	--temp;  
	return *temp;
}

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

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

self operator++(int)
{
	self temp(_it);
	--_it;
	return temp;
}

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

self operator--(int)
{
	self temp(_it);
	++_it;
	return temp;
}

bool operator!=(const self l)
{
	return _it != l._it;
}

bool operator==(const self l)
{
	return _it == l._it;
}
Iterator _it;
};

template<class T>
class list{
public:
typedef ListNode<T> Node;
typedef Reverse_Iterator<iterator> reverse_iterator;
typedef Reverse_Iterator<const_iterator> const_reverse_iterator;
reverse_iterator rbegin()
{
	return reverse_iterator(_head->_pre);
}

reverse_iterator rend()
{
	return reverse_iterator(_head->_next);
}

const_reverse_iterator rbegin()const
{
	return const_reverse_iterator(_head->_pre);
}

const_reverse_iterator rend()const
{
	return const_reverse_iterator(_head->_next);
}
};

四、常见函数及其实现

1.insert函数

在pos位置前插入一个值为val的结点。
最后更新_size
注意插入完返回插入后结点的迭代器,防止出现迭代器失效问题。

iterator insert(iterator pos,const T& val)
{
	Node* temp = new Node;
	temp->_val = val;
	temp->_pre = pos._node->_pre;
	pos._node->_pre->_next = temp;
	temp->_next = pos._node;
	pos._node->_pre = temp;
	_size++;
	return iterator(temp);
}

2.erase函数

删除pos当前位置的结点,并释放它的空间。
最后更新_size
注意:返回pos的下一个结点的迭代器,否则会出现迭代器失效。

iterator erase(iterator pos)
{
	Node* p = pos._node->_next;
	p->_pre = pos._node->_pre;
	pos._node->_pre->_next = p;
	delete pos._node;
	pos = nullptr;
	_size--;
	return iterator(p);
}

3.clear函数

依次遍历删除每一个结点,并释放它们的空间。
最后重置一下_head的_next和_pre指针,防止出错。

void clear()
{
	Node* p = _head->_next;
	while (p != _head)
	{
		_head->_next = p->_next;
		p->_next->_pre = _head;
		delete p;
		p = _head->_next;
		_size--;
	}
	_head->_next = _head;
	_head->_pre = _head;
}

4.push_back和push_front函数

直接调用insert函数即可。

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

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

5.pop_back和pop_front 函数

直接调用erase函数即可。
注意pop_back应传入最后一个元素的迭代器,即_head->_pre。

void pop_back()
{
	erase(end()._node->_pre);
}

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

6.resize函数

1.若newsize小于_size,则直接pop_back
2. 若newsize大于_size,则直接push_back
直到newsize==size

void resize(size_t newsize, const T& val=T())
{
	if (newsize < _size)
	{
		while (newsize != _size)
			pop_back();
	}
	else
	{
		while (newsize != _size)
			push_back(val);
	}
}

7.front和back函数

因为链表不支持随机访问,但其访问首元素和尾元素的效率很高。
front返回首元素的值,back返回尾元素的值。

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

8.swap,empty和size函数

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

size_t size()
{
	return _size;
}

bool empty()
{
	return _size == 0;
}

总结

以上就是list的常用函数以及模拟实现(本人小白一枚,若有错误还望各位指正)
完整的代码存放在gitee上:https://gitee.com/jj-wen/c-beginner/blob/master/06.21%EF%BC%88list%EF%BC%89/06.21%EF%BC%88list%EF%BC%89/list.h
学习任重而道远,希望自己可以坚持下去。

标签:node,head,return,temp,list,C++,next,const,模拟
From: https://blog.csdn.net/m0_52420323/article/details/139855952

相关文章

  • 我一直看不明白:“C++会被java/python等这些语言替代”
    在开始前刚好我有一些资料,是我根据网友给的问题精心整理了一份「C++的资料从专业入门到高级教程」,点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!有些程序,是既可以用c++编写,也可以用java/python编写。如果这类程序以前主要是由c++编写,后来逐渐变成主要......
  • c++重载输出流(<<)
    一.重载输出流在C++中,可以重载输出流运算符(<<)来自定义对象的输出方式。重载输出流运算符允许我们以自定义的方式将对象输出到标准输出流或其他输出流中。以下是关于重载输出流运算符(<<)的几个知识点以及相应的示例:重载输出流运算符的语法:重载输出流运算符必须作为一个普......
  • 《C++ Primer》导学系列:第 7 章 - 类
    7.1定义抽象数据类型7.1.1类的基本概念在C++中,类是用户定义的类型,提供了一种将数据和操作这些数据的函数(成员函数)组合在一起的方法。类定义了对象的属性和行为,通过实例化类来创建对象。7.1.2定义类定义类时,需要指定类的名称,并在类体内声明数据成员和成员函数。类定义的......
  • C++的封装(适合新人,通俗易懂)
    作者:求一个demo版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处内容通俗易懂,没有废话,文章最后是面试常问内容1、访问权限介绍封装,那么需要先介绍一下访问权限:public公共权限、protected保护权限和private私有权限。(1)public公共权限简单来说:如果......
  • A tour of C++ 读书笔记
    第一章:C++只是个编译型语言,需要源文件编译成目标文件,再通过链接各种库到可执行文件1.6常量  const  constexpr这个代表是要在编译的时候估值,性能会有所增加吧2.4联合体(union)  联合体所有的成员都是分配在同一地址上面,所以联合体所占的空间是跟其自身内部成员所......
  • C++入门(万字总结,建议收藏!!!)
    一、前言1.1什么是C++C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机,20世纪80年代,计算机界提出了OOP(objectorientedprogramming:面向对象)思想,支持面向对象的程序设计语言应运......
  • 计算机毕业设计项目推荐,33709基于协同过滤的旅游推荐系统的设计与实现(开题答辩+程序定
    摘 要本论文主要论述了如何使用python语言、Django框架开发一个旅游推荐系统,本系统将严格按照软件开发流程,进行各个阶段的工作,面向对象编程思想进行项目开发。在引言中,作者将论述该系统的当前背景以及系统开发的目的,后续章节将严格按照软件开发流程,对系统进行各个阶段分析......
  • C++ 面向对象高级开发 2、头文件与类的声明
       ObjectBased(基于对象)vs ObjectOriented(面向对象)ObjectBased:面对的是单一class的设计ObjectOriented:面对的是多重classes的设计,classes和classes之间的关系。         模板就是一种抽象......
  • C++ 面向对象高级开发 3、构造函数
    1、内联函数inline 内联函数速度比较快 最终是不是inline实际上是由编译器决定的。 一般比较简单,编译器就能确定inline函数 2、AccessLevel访问级别  3、构造函数Construct默认实参。Defaultargument.充分利用构造函数的特殊语法,对数据进行初始化,这是一种比......
  • C++数据格式化5 - uint转换成十六进制字符串&二进制的data打印成十六进制字符串
    1.关键词2.strfmt.h3.strfmt.cpp4.测试代码5.运行结果6.源码地址1.关键词关键字:C++数据格式化字符串处理std::stringinthex跨平台应用场景:int型的数据打印成十六进制字符串二进制的data打印成十六进制字符串。2.strfmt.h#pragmaonce#include<stri......