目录
[2.7.1 insert插入](#2.7.1 insert插入)
一.什么是List

二.List的模拟实现
2.1List的基本结构


问题一:为什么要分成两个类来写
1)
ListNode(节点类)只负责 一个节点自己的事:
- 存数据
_data- 存前驱指针
_prev- 存后继指针
_next- 只关心:我是谁、我前后是谁
2)
List(链表管理类)负责 整个链表的事:
- 维护头结点
_head- 维护长度
_size- 提供接口:
push_back、insert、erase、遍历......- 只关心:怎么管理一群节点、怎么对外提供服务
如果把节点和链表写在同一个类里:
- 数据、
prev、next、head、size、所有成员函数 全部挤在一起- 分不清 "节点本身" 和 "整个链表"
- 代码混乱、难读、难改、复用性差
对比(一眼看出区别)
- 分开写 :Node 只管节点;List 只管链表 → 清晰、各司其职
- 写一起 :什么都混在一起 → 一坨面条代码
问题二:ListNode<T>* _next,这里为什么要加T

2.2构造+尾插

这里运行不了,运行不了的原因是
list_node 缺少无参构造函数
在 List.h 中,list_node 结构体的构造函数只有带参数的版本(list_node(const T& x)),但没有无参构造函数。
而在 list 类的构造函数中,执行了 _head = new Node; ------ 这里尝试调用 list_node 的无参构造函数,但该构造函数并不存在,因此会触发编译错误。
方法 1:利用list_node的带参构造显式传入默认值
在 list 类的构造函数中,创建头结点时,显式调用 list_node 的带参构造,并传入 T 类型的默认值(通过 T() 触发 T 的默认构造,也就是使用匿名对象,T有可能是任意类型)。这样无需为 list_node 新增无参构造函数。
cpp
template<class T>
class list
{
public:
list()
{
// 调用list_node的带参构造,传入T的默认值
_head = new Node(T());
_head->_prev = _head;
_head->_next = _head;
}
// 其余成员保持不变...
};
方法2:使用委托构造函数(C++11 及以后)
让带参数的构造函数也能处理无参数的情况,通过委托给自己并提供一个默认值
cpp
template<class T>
struct list_node
{
list_node<T>* _prev;
list_node<T>* _next;
T _data;
// 带参数的构造函数,并提供一个默认参数 T()
list_node(const T& x = T())
: _prev(nullptr)
, _next(nullptr)
, _data(x)
{}
};
2.3迭代器




单独写一个迭代器的类是因为他不能像vector那样++,*等,List的地址是不连续的,++的话只能到它相邻的下一个,但是链表不是连续的地址,vector是连续的,可以支持++,所以我们需要创建一个单独的类来实现这种相同的++效果
这个 List_iterator 类,就是为了给你的双向链表提供「迭代器」功能,让链表也能像 vector 一样,用统一的方式遍历元素。
1. 封装底层细节,隐藏链表的实现
用户用迭代器时,只需要关心 ++it、*it,不需要知道底层是 _next 还是 _prev 指针。
- 迭代器类把
_node->_next封装在operator++()里,用户只需要写++it就行。 - 这样就算你以后改了链表的底层实现(比如改成单向链表),只要迭代器接口不变,用户的代码完全不用改。
2. 实现双向迭代器的完整功能
你的 List 是双向链表,迭代器需要支持:
++it前进(你已经实现了前置 ++)--it后退(你还没写,但后续可以加)*it访问数据it != it2、it == it2比较it->访问成员(需要额外实现operator->)
如果直接用裸指针,这些功能需要用户自己手动实现,既麻烦又容易出错;而封装成迭代器类后,这些操作都被标准化了。
| 函数 | 作用 |
|---|---|
List_iterator(Node* node) |
构造函数:把迭代器绑定到一个链表节点上 |
iterator& operator++() |
前置 ++:让迭代器移动到下一个节点(通过 _node->_next) |
* T& operator*() |
解引用:返回节点存储的数据 _data |
bool operator!=(...) |
比较两个迭代器是否指向不同节点 |
bool operator==(...) |
比较两个迭代器是否指向同一个节点 |
this 指针和 *this 是什么?
在 C++ 的非静态成员函数中,编译器会自动给函数传递一个隐含的指针参数 ------this,它的作用是:指向调用这个成员函数的「对象本身」。
举个具体例子:假设你定义了迭代器对象 it,然后调用 ++it,此时 it 就是调用 operator++() 函数的对象,函数里的 this 指针就指向 it 这个对象(this 的类型是 list_iterator< T >*)。
而 *this 就是对 this 指针做「解引用」,得到的是:调用该函数的那个迭代器对象本身(类型是 list_iterator< T >)。
2.4插入+删除+头插


2.5->运算符重载

如果链表中存放的是结构体,要想获取里面的数据就要这样写,先解引用获取,然后再获取里面的数据,能不能直接用操作符直接获取,这是我们就需要重载运算符->



2.6const类型的迭代器


为什么这里会报错呢?
这是因为在Print_Container的形参是const类型修饰的,但这里传入的是非const类型的,所以还需是现const的迭代器
这里的typename是什么意思



const的用法:

我们发现const迭代器和非const迭代器的相似度很高,写两个显得冗余,是否能创建一个模板来实现两个迭代器通用?


2.7迭代器失效问题
2.7.1 insert插入

我们可以发现插入的时候迭代器未失效

2.7.2erase删除

为什么这里的迭代器失效了

这时就需要更新迭代器的指向了