C++:list类(迭代器)

目录

前言

数据结构

push_back

push_front

默认构造函数

拷贝构造函数

list迭代器

结构 

构造函数

*运算符重载

->运算符重载

前置++运算符重载

后置++运算符重载

前置--运算符重载

后置--运算符重载

!=运算符重载

==运算符重载

list迭代器完整代码

begin和end

swap

赋值重载函数

insert

erase

迭代器失效问题

pop_back

pop_front

clear

析构函数

size

empty

完整代码


前言

list是链表的意思

它属于链表中的带头双向循环链表

建议先掌握数据结构中的链表

C数据结构:单链表-优快云博客

C数据结构:双向链表(带头循环)_c带头双向循环链表-优快云博客

数据结构

首先我们需要一个链表的节点

template<class T>
struct ListNode
{
	T _data;
	ListNode* _next;
	ListNode* _prev;

	ListNode(const T& data = T())
		:_data(data)
		,_next(nullptr)
		,_prev(nullptr)
	{}
};

因为这个ListNode类节点里面的内容可以是公开的,所以使用了struct默认即为public,当然也可以使用class来定义类,只需注意使用访问限定符即可

双向链表当然需要next和prev两个指针来指向后面和前面

类中还定义了一个默认构造函数用来构造节点

成员变量和成员函数放置的顺序前后都是可以的,但通常会把成员变量放到最下面

template<class T>
class list
{
    typedef ListNode<T> Node;
public:

private:
	Node* _head;
	size_t _size = 0;
};

Node*指针指向链表头节点,我们还可以定义一个size成员函数,这样可以在计算链表大小的时候减少遍历节点次数

push_back

void push_back(const T& data)
{
	Node* newnode = new Node(data);
	Node* tail = _head->prev;

	tail->_next = newnode;
	newnode->_next = _head;
	newnode->_prev = tail;
	_head->_prev = newnode;
	_size++;
}

生成新节点,将它插入到最后面即可 

push_front

void push_front(const T& data)
{
	Node* newnode = new Node;
	Node* next = _head->_next;

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

	insert(begin(), data);
}

生成新节点,插入到_head的下一个位置即可

默认构造函数

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

构造出我们的哨兵位头节点,并初始化next和prev指针和size大小

因为这段默认构造函数在其他的成员函数内部也可能会使用,并且默认构造函数不方便显示调用,所以我们可以把这段代码用一个函数来封装起来,方便其他成员函数使用

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

list()
{
	EmptyInit();
}

这样我们把空初始化的逻辑放到EmptyInit函数中即可

拷贝构造函数

list(const list<T>& lt)
	:_head(lt._head)
	,_size(lt._size)
{}

这是经典的错误写法! 

这里只是将lt里面的_head按字节拷贝给了this的_head(浅拷贝)

所以最终是两个_head用同一个链表,同一块空间

为了避免这个问题,我们需要重新生成一段空间来完成深拷贝

list(const list<T>& lt)
{
	EmptyInit();

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

复用EmptyInit和push_back的逻辑

因为EmptyInit会初始化出一个哨兵位头节点,我们只需要在这个头节点后面依次插入lt里面的元素即可,因为push_back也是会开新空间的

但是这里的遍历是还完不成的,因为范围for需要有begin,end,重载!=函数,这些函数又需要我们的迭代器,但是我们类中目前是还没有实现的,所以我们还需要先实现完成迭代器和这些成员函数才能完成这个拷贝构造

list迭代器

这里迭代器的实现就没有前面vector和string的那么简单了,因为它们两个的迭代器都可以通过指针的+-来遍历整个容器,但是list由于空间的不连续,所以不能简单的只是使用指针进行+-运算,而是需要我们手动完成对++,--这些运算符的重载操作

结构 

template<class T, class Ref, class Ptr>
struct list_iterator
{
	typedef ListNode<T> Node;
	typedef list_iterator<T, Ref, Ptr> Self;

	Node* _node;
};

这里需要有三个模板的支撑&#x

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ragef

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值