0、前言:
类中的 list 是双向带头循环链表
我们这里模拟实现的 list 也是 双向带头循环链表
1、list 的核心框架实现(list 类模板)
1.1 节点类模板 和 链表类模板
1.1.1 节点类
双向链表节点(含有 prev 指针 和 next 指针)
template<class T> struct ListNode { typedef ListNode<T> Node; Node* _next; Node* _prev; T _data; // 构造函数 ListNode(const T& data = T()) :_data(data) , _next(nullptr) , _prev(nullptr) {} };
【思考】为什么 节点类 用的是 struct ,而不是 class?
先说明:struct 和 class 的区别
因为 struct 在 C++语法中,和 class 都可以表示类
struct 是C++兼容 C语言的 语法
在 C++中 struct 的类,没有访问限定符的限制
所有成员一律 视为公有
再说明:为什么节点类要用 struct
依据C语言学过的链表,在一个链表节点中,指向前后节点的指针和存储该节点数据的变量 都需要频繁的被外界访问(_next、_prev、_data),与其在 class 中使用 public 访问限定符,不如干脆直接使用 struct ,使该类中的所有成员都可被外界直接,提高便捷性
1.1.2 链表类
带头链表:含有一个 哨兵位节点,成员变量 _head 指向该哨兵位节点
template<class T> class list { typedef ListNode<T> Node; // 将节点类型重命名 private: Node* _head; };
1.2 构造函数
作用:初始化一个 头节点(即哨兵位)
// 构造函数:创建头节点 list() { empty_list(); _head = new Node(); _head->_next = _head; _head->_prev = _head; // 注意:_next 和 _prev 都是指向 _head,不是 nullptr !!!,否则 push_back 时会 有访问空指针的问题 }
2. 迭代器类模板(重点:理解迭代器思想)
2.1 迭代器思想与封装思想
你一个内置类型,无法完成的操作与行为,可以将该类型封装成一个类,在类里面重载各种运算,使这个内置类型间接拥有之前没有 “功能”
例如:
在链表中,有指向一个节点的指针 Node* _node;
这个指针是不能直接通过 ++ 操作,跳转到下一个节点的(链表空间不连续,不能像 vector 一样通过 原生指针 T* 的 ++操作 到达下一个元素位置)
此时可以将该指针封装成一个类,在类里面重载++操作,间接的使指针 _ node 有了 [ 指针++ ] 就跳转到下个节点的 功能(因为重载函数中的操作:_node = _ node-> _next; 使 _node 指向下一个节点位置)
template<class T> struct NewPtr { typedef ListNode<T> Node; typedef NewPtr<T> Self; // 自己这种类型 Node* _node; // 核心还是 一个链表节点指针 // 构造函数 // 这里的 p 不用给 const ,否则权限放大 NewPtr(Node* p = nullptr) :_node(p) {} Self& operator++() { _node = _node->_next; return *this; } };
小结:
内置类型不能直接使用的行为,我们可以将这个内置类型封装成一个类型,在类型中通过重载运算符控制它的行为
因此:将 list 的节点指针 封装成一个迭代器类,使该指针拥有某些之前无法单独实现的 功能
迭代器的表面上是一个类型的壳
在内心实际上是一个节点的指针
2.2 链表的迭代器 ListIterator
按上面的逻辑:设计链表的迭代器 ListIterator
// 迭代器就是将指向一个节点的指针 node 封装成一个类 template<class T> struct ListIterator { typedef ListNode<T> Node; typedef ListIterator<T> Self; // 自己这种类型 Node* _node; // 构造函数 // 这里的 p 不用给 const ,否则权限放大 ListIterator(Node* p = nullptr) :_node(p) {} // ++ -- Self& operator++() { _node = _node->_next; return *this; } };