前言
在学习了 list 容器的基本使用的前提下,本文将重点分析容器类的常用接口及其应用实现。
一、list 的成员变量
list 的底层数据结构为双向链表。

通过下面三个结构体和类实现 list:
- 结构体 struct list_node:用来存储链表结点的信息。
- 结构体 struct list_iterator:用来封装结点指针,使其能够通过重载运算符访问结点。
- 类 class list:用来实现双向链表的各种增删查改操作。
1.1 结构体 list_node
template<class T> struct list_node { //对 struct list_node 重命名为 Node typedef list_node<T> Node; //链表存储的节点值 T _data; //前驱指针 Node* _prev; //后继指针 Node* _next; };
对于双向链表的结点 struct list_node:
- 前驱指针:Node* _prev
- 后继指针:Node* _next
- 链表存储的节点值:T _data
typedef 将 list_node 重命名为 Node 以简化代码。

1.2 结构体 list_iterator
//迭代器:对结点指针进行封装 template<class T, class Ref, class Ptr> struct list_iterator { //将 list_node 结构体类型重命名为 Node typedef list_node<T> Node; //将 list_iterator 结构体类型重命名为 Self typedef list_iterator< T, Ref, Ptr> Self; //定义结点指针 Node* _pnode; };
对于双向链表的迭代器 struct list_iterator:
- 重命名结构体名:typedef list_node Node,typedef list_iterator< T, Ref, Ptr> Self。
- 核心成员变量:Node* _pnode。
- 模板参数分析:template<class T, class Ref, class Ptr>
- class T:用于接收容器中实际存储的元素类型。
- class Ref:迭代器解引用操作符 operator*() 的返回类型。普通迭代器传入 T&(允许修改),常量迭代器传入 const T&(只读)。
- class Ptr:迭代器箭头操作符 operator->() 的返回类型。普通迭代器传入 T*,常量迭代器传入 const T*。
1.3 类 class list
//链表 template<class T> class list { public: //将 list_node 结构体类型重命名为 Node typedef list_node<T> Node; //将 list_iterator 结构体类型重命名为 iterator typedef list_iterator<T, T&, T*> iterator; //将 list_iterator 结构体类型重命名为 const_iterator typedef list_iterator<T, const T&, const T*> const_iterator; private: //哨兵位结点指针 Node* _head; };
双向链表 class list:
- 重命名结构体名:typedef list_node Node,typedef list_iterator<T, T&, T*> iterator,typedef list_iterator<T, const T&, const T*> const_iterator。
- 核心变量 Node* _head:哨兵位结点指针。
二、默认成员函数
2.1 list_node 的构造函数
list_node 的默认构造函数:
//默认构造函数 list_node(const T& data = T(), Node* prev = nullptr, Node* next = nullptr) : _data(data) , _prev(prev) , _next(next) {}
- const T& data = T() (数据部分):T 是模板类型。const T& 表示常引用接收数据,避免拷贝开销。data= T() 为默认值,若 T 是 int 则为 0,指针则为空。
- Node* prev = nullptr (前驱指针)
- Node* next = nullptr (后继指针)
2.2 list_iterator 的构造函数
list_iterator 的默认构造:
//迭代器的默认构造函数 list_iterator(Node* pnode = nullptr) :_pnode(pnode) {}
list_iterator 的普通构造:允许从普通迭代器构造 const 迭代器
// 允许从普通迭代器构造 const 迭代器 list_iterator(const list_iterator<T, T&, T*>& it) : _pnode(it._pnode) {}
2.3 class list 的构造函数
list 的无参构造:
template<class T> void list<T>::CreateHead() { //1.这里会自动调用带有默认参数的构造函数:data 为 T(), prev 为 nullptr, next 为 nullptr _head = new Node; //2.然后再手动将其构造成一个自循环的结构 _head->_next = _head; _head->_prev = _head; } //无参构造 list() { CreateHead(); }
list 的拷贝构造:
//拷贝构造函数 list(const list& lt) { CreateHead(); for (const auto& elem : lt) { push_back(elem); } }
list 的赋值重载:
template<class T> void list<T>::swap(list<T>& lt) { std::swap(_head, lt._head); } //赋值重载函数 list<T>& operator=(list<T> lt) { swap(lt); return *this; }
list 的析构函数:
//析构函数 ~list() { //释放除哨兵结点以外的结点 clear(); //释放哨兵节点 delete _head; //将哨兵_head 指针置空 _head = nullptr; }
三、list 的迭代器
3.1 重载运算符*
函数原型:Ref operator*() 函数功能:返回迭代器所指向的值。
- 如果是普通迭代器,Ref 被推导为 T&。
- 如果是常量迭代器,Ref 被推导为 const T&。
模拟实现指针解引用的功能:当用户对我的迭代器使用 * 号时,去底层节点里把真实的数据拿出来给他。
代码实现:重载运算符*
//运算符重载 Ref operator*() { return _pnode->_data; }
功能演示:
// 假设这是你写代码时的场景 list<int>::iterator it = mylist.begin(); // 场景 1:读取数据 cout << *it << endl; // 发生的事情:调用 operator*() -> 找到 _pnode -> 返回 _data 的引用 -> 打印出来 // 场景 2:修改数据 (如果 it 是普通迭代器) *it = 999; // 发生的事情:调用 operator*() -> 返回了 _data 的引用 (int&) -> 将 999 赋值给这个引用 -> 节点里的数据真正变成了 999
3.2 重载运算符->
函数原型:Ptr operator->() 函数功能:返回迭代器所指向的元素的地址。
- 如果是普通迭代器,Ptr 被推导为 T*,可以通过箭头修改对象的成员。
- 如果是常量迭代器,Ptr 被推导为 const T*,只能通过箭头读取成员。
代码实现:重载运算符->
Ptr operator->() { return &(_pnode->_data); }
C++ 中 operator-> 的'魔法':如果 operator-> 返回的是一个指针,编译器会自动在后面再加一个隐式的 ->。
假设你链表里存的是一个结构体:
struct Student { string name; int age; };
当你在代码中写下 it->age 时,编译器在底层到底做了什么?
- 编译器看到 it->age。
- 编译器调用 it.operator->(),得到了真实数据的指针:Student* p = &(_pnode->_data)。
- 编译器自动帮你对这个指针再调用一次箭头,变成了 p->age。
我们只需要简单地返回数据的地址 &(_pnode->_data),用户端用起来就极其自然。
3.3 重载运算符前置++
函数原型:Self& operator++() 函数功能:目的是让迭代器指向下一个节点。
- Self:typedef list_iterator<T, Ref, Ptr> Self 的别名定义。
- Self&:既支持了 C++ 的连续调用语法(链式操作),通过引用返回减少了拷贝的消耗。
代码实现:重载运算符前置++
Self& operator++() { _pnode = _pnode->_next; return *this; }
3.4 重载运算符后置++
函数原型:Self operator++(int) 函数功能:目的是让迭代器指向下一个节点。
代码实现:重载运算符后置++
Self operator++(int) { Self tmp = *this; ++(*this); return tmp; }
3.5 重载运算符前置--
函数原型:Self& operator--() 函数功能:目的是让迭代器指向上一个节点。
代码实现:重载运算符前置--
Self& operator--() { _pnode = _pnode->_prev; return *this; }
3.6 重载运算符后置--
函数原型:Self operator--(int) 函数功能:目的是让迭代器指向上一个节点。
代码实现:重载运算符后置--
Self operator--(int) { Self tmp = *this; --(*this); return tmp; }
3.7 重载运算符!=
函数原型:bool operator!=(const Self& s) 函数功能:判断两个迭代器指向的位置是否不同。
代码实现:重载运算符!=
bool operator!=(const Self& s) { return _pnode != s._pnode; }
3.8 重载运算符==
函数原型:bool operator==(const Self& s) 函数功能:判断两个迭代器指向的位置是否相同。
代码实现:重载运算符==
bool operator==(const Self& s) { return _pnode == s._pnode; }
四、list 相关函数操作
4.1 begin
函数原型:
- iterator begin();
- const_iterator begin() const
函数功能:返回一个指向 list 中第一个元素的迭代器。
代码实现:
iterator begin() { //指向容器中第一个元素的迭代器 return iterator(_head->_next); //返回匿名对象 } const_iterator begin() const { return const_iterator(_head->_next); //返回匿名对象 }
4.2 end
函数原型:
- iterator end();
- const_iterator end() const
函数功能:返回一个指向 vector 容器中最后一个元素之后的元素。
代码实现:
iterator end() { //指向容器中最后一个元素之后位置的迭代器 return iterator(_head); //返回匿名对象 } const_iterator end() const { return const_iterator(_head); //返回匿名对象 }
4.3 insert
函数原型: template iterator insert (iterator position, const T& val);
函数功能:容器通过在指定位置之前插入新元素来扩展。
代码实现:insert 函数
template<class T> typename list<T>::iterator list<T>::insert(iterator pos, const T& x) { //申请一个新结点 Node* newnode = new Node(x); //pos 位置结点指针 Node* cur = pos._pnode; newnode->_prev = cur->_prev; newnode->_next = cur; cur->_prev->_next = newnode; cur->_prev = newnode; return iterator(newnode); }

4.4 erase
函数原型:iterator erase (iterator position);
函数功能:从 list 容器中删除单个元素(位置)。
代码实现:erase 函数
template<class T> typename list<T>::iterator list<T>::erase(iterator pos) { //不能删除哨兵结点 assert(pos._pnode != _head); //pos 位置的结点指针 Node* cur = pos._pnode; //pos 位置前一个位置的结点指针 Node* prev = cur->_prev; //pos 位置后一个位置的结点指针 Node* next = cur->_next; prev->_next = next; next->_prev = prev; //cur->_prev->_next = cur->_next; //cur->_next->_prev = cur->_prev; delete cur; return iterator(next); //通过匿名变量,返回下一个位置的结点指针 }

4.5 push_back
函数原型: template void push_back (const T& val);
函数功能:在末尾添加元素。
代码实现:通过复用 insert 实现尾插元素
template<class T> void list<T>::push_back(const T& x) { insert(end(), x); }

4.6 push_front
函数原型: template void push_front (const T& val);
函数功能:在开头插入元素。
代码实现:通过复用 insert 实现头插元素
template<class T> void list<T>::push_front(const T& x) { insert(begin(), x); }

4.7 pop_back
函数原型:void pop_back();
函数功能:删除最后一个元素。
代码实现:通过复用 erase 函数实现尾删元素
template<class T> void list<T>::pop_back() { erase(--end()); }

4.8 pop_front
函数原型:void pop_front();
函数功能:在开头删除元素。
代码实现:复用 erase 实现头删元素
template<class T> void list<T>::pop_front() { erase(begin()); }

4.9 clear
函数原型:void clear();
函数功能:从 list 容器中移除所有元素(这些元素将被销毁),使容器的大小变为 0。
代码实现:list 容器中移除所有元素
template<class T> void list<T>::clear() { //it 可能是 const_iterator 或则 iterator auto it = begin(); while (it != end()) { // erase 返回的是被删结点的下一个位置,所以迭代器不会失效 it=erase(it); } }


