STL详解 - list的模拟实现
目录
1. list 的基本结构
1.1 构造函数
2. 迭代器的实现
2.1 构造函数
2.2 自增和自减操作符
2.3 比较操作符
2.4 解引用和箭头操作符
3. list 容器的实现
3.1 构造函数
3.2 拷贝构造
3.3 赋值运算符重载
3.4 析构函数
3.5 迭代器相关函数
3.6 插入和删除函数
3.7 其他函数
4. 测试代码
5. 源码
1. list 的基本结构
list
是一个双向链表,每个节点包含两个指针,分别指向其前驱节点和后继节点。我们首先定义一个 ListNode
结构来表示链表中的节点。
template<class T>
struct ListNode
{ListNode<T>* _next; // 指向后继节点ListNode<T>* _prev; // 指向前驱节点T _data; // 节点存储的数据// 构造函数ListNode(const T& x = T()):_next(nullptr),_prev(nullptr),_data(x){}
};
1.1 构造函数
构造函数用于初始化节点。如果未传入数据,则使用默认构造函数初始化数据域。前驱和后继指针初始化为
nullptr
。
ListNode(const T& x = T()):_next(nullptr),_prev(nullptr),_data(x)
{}
2. 迭代器的实现
为了方便遍历链表,我们需要定义一个迭代器结构。迭代器需要支持基本的操作,如解引用、自增、自减等。
template<class T, class Ref, class Ptr>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node; // 当前节点指针// 构造函数ListIterator(Node* node):_node(node){}// 解引用操作符Ref operator*(){return _node->_data;}// 箭头操作符Ptr operator->(){return &_node->_data;}// 前置自增Self& operator++(){_node = _node->_next;return *this;}// 后置自增Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}// 前置自减Self& operator--(){_node = _node->_prev;return *this;}// 后置自减Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}// 比较操作符bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}
};
2.1 构造函数
迭代器的构造函数接收一个节点指针,并将其存储在
_node
中。
ListIterator(Node* node):_node(node)
{}
2.2 自增和自减操作符
自增和自减操作符分别用于移动迭代器到下一个或前一个节点。
Self& operator++()
{_node = _node->_next;return *this;
}Self operator++(int)
{Self tmp(*this);_node = _node->_next;return tmp;
}Self& operator--()
{_node = _node->_prev;return *this;
}Self operator--(int)
{Self tmp(*this);_node = _node->_prev;return tmp;
}
2.3 比较操作符
比较操作符用于判断两个迭代器是否指向同一个节点。
bool operator!=(const Self& it)
{return _node != it._node;
}bool operator==(const Self& it)
{return _node == it._node;
}
2.4 解引用和箭头操作符
解引用和箭头操作符用于访问节点中的数据。
Ref operator*()
{return _node->_data;
}Ptr operator->()
{return &_node->_data;
}
3. list 容器的实现
list
容器需要管理链表的头节点,并提供插入、删除、遍历等操作。
template<class T>
class list
{
public:typedef ListNode<T> Node;typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;// 迭代器相关函数iterator begin(){return _head->_next;}iterator end(){return _head;}const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}// 构造函数list(){empty_init();}// 拷贝构造list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}// 赋值运算符重载list<T>& operator=(list<T>& lt){swap(lt);return *this;}// 析构函数~list(){clear();delete _head;_head = nullptr;}// 插入、删除函数void insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;}iterator erase(iterator pos){Node* cur = pos._node;Node* next = cur->_next;Node* prev = cur->_prev;prev->_next = next;next->_prev = prev;delete cur;_size--;return iterator(next);}void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}// 其他函数size_t size() const{return _size;}bool empty(){return _size == 0;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}private:Node* _head; // 指向链表头结点的指针size_t _size; // 链表大小// 初始化空链表void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}
};
3.1 构造函数
初始化一个空链表,确保链表的头节点
_head
被正确创建。头节点的前驱和后继指针都指向自身,形成一个循环链表。
初始化
_size
为 0,表示链表为空。
// 初始化空链表
void empty_init()
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;
}
list()
{empty_init();
}
3.2 拷贝构造
首先调用
empty_init()
初始化一个空链表。遍历源列表
lt
,将每个元素通过push_back
插入到新列表中。这种方式确保新列表与源列表具有相同的元素顺序。
list(const list<T>& lt)
{empty_init();for (auto& e : lt){push_back(e);}
}
3.3 赋值运算符重载
赋值运算符重载函数通过交换两个列表的头节点来实现高效赋值。
通过交换两个列表的头节点
_head
和大小_size
,可以高效地完成赋值操作。这种方式避免了逐个元素的复制,提高了效率。
调用
swap
函数后,原列表的内容被交换到当前列表中。
list<T>& operator=(list<T>& lt)
{swap(lt);return *this;
}
3.4 析构函数
首先调用
clear()
清空列表,释放所有节点。删除头节点
_head
,并将其置为nullptr
,避免悬空指针。
~list()
{clear();delete _head;_head = nullptr;
}
3.5 迭代器相关函数
begin()
返回头节点的下一个节点,即第一个有效节点。
end()
返回头节点本身,表示迭代的结束位置。
iterator begin()
{return _head->_next;
}iterator end()
{return _head;
}
3.6 插入和删除函数
insert
函数在指定位置插入一个新节点。
获取当前节点
cur
和其前驱节点prev
。创建一个新节点
newnode
,并初始化其数据和指针。调整
prev
和cur
的指针,将新节点插入到链表中。更新链表大小
_size
。
void insert(iterator pos, const T& val)
{Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;
}
erase
函数删除指定位置的节点。
获取当前节点
cur
、其前驱节点prev
和后继节点next
。调整
prev
和next
的指针,绕过当前节点。删除当前节点
cur
,并更新链表大小_size
。返回下一个节点的迭代器,方便链式操作。
iterator erase(iterator pos)
{Node* cur = pos._node;Node* next = cur->_next;Node* prev = cur->_prev;prev->_next = next;next->_prev = prev;delete cur;_size--;return iterator(next);// 返回下一位置迭代器
}
3.7 其他函数
push_back
和pop_back
分别用于尾插和尾删。
push_back
调用insert
在链表末尾插入新节点。
pop_back
调用erase
删除链表末尾的节点。
void push_back(const T& x)
{insert(end(), x);
}void pop_back()
{erase(--end());
}
push_front
和pop_front
分别用于头插和头删。
push_front
调用insert
在链表头部插入新节点。
pop_front
调用erase
删除链表头部的节点。
void push_front(const T& x)
{insert(begin(), x);
}void pop_front()
{erase(begin());
}
size
函数返回列表中有效节点的数量。
直接返回链表大小
_size
,提供高效的大小查询。
size_t size() const
{return _size;
}
empty
函数判断列表是否为空。
检查链表大小
_size
是否为 0,快速判断链表是否为空。
bool empty()
{return _size == 0;
}
clear
函数清空列表。
遍历链表,逐个删除节点,直到链表为空。
void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}
swap
函数交换两个列表的头节点。
通过交换头节点和大小,高效地交换两个列表的内容。
void swap(list<T>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}
4. 测试代码
void test_list1()
{// 基础功能测试list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);// 遍历输出list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;// 添加元素lt.push_back(10);lt.push_back(20);lt.push_front(30);lt.push_front(40);// 使用范围 for 遍历for (auto e : lt){cout << e << " ";}cout << endl;// 删除元素lt.pop_back();lt.pop_back();lt.pop_front();lt.pop_front();// 再次遍历输出for (auto e : lt){cout << e << " ";}cout << endl;
}// 自定义类型测试
struct A
{int _a1;int _a2;A(int a1 = 0, int a2 = 0):_a1(a1),_a2(a2){}
};void test_list2()
{list<A> lt;A aa1(2, 2);A aa2 = {3, 3};lt.push_back(aa1);lt.push_back(aa2);lt.push_back(A(4, 4));lt.push_back({5, 5});lt.push_back({6, 6});// 遍历输出结构体成员list<A>::iterator it = lt.begin();while (it != lt.end()){cout << it->_a1 << ":" << it->_a2 << endl;++it;}
}void PrintList(const list<int>& clt)
{list<int>::const_iterator it = clt.begin();while (it != clt.end()){cout << *it << " ";++it;}cout << endl;
}void test_list3()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int> lt1(lt);PrintList(lt1);
}
5. 源码
#pragma once // 防止头文件重复包含#include <assert.h> // 断言相关函数
#include <iostream> // 输入输出流
using namespace std;namespace lv // 命名空间lv
{// 链表节点模板类template<class T>struct ListNode{ListNode<T>* _next; // 后继指针ListNode<T>* _prev; // 前驱指针T _data; // 数据域// 构造函数,初始化节点,默认数据为T类型的默认构造值ListNode(const T& x = T()):_next(nullptr),_prev(nullptr),_data(x){}};// 链表迭代器模板类(泛型设计,支持普通/const迭代器)template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node; // 节点类型别名typedef ListIterator<T, Ref, Ptr> Self; // 自身类型别名Node* _node; // 当前节点指针// 构造函数,用给定节点指针初始化迭代器ListIterator(Node* node):_node(node){}// 解引用运算符,返回数据引用(Ref可能是T&或const T&)Ref operator*(){return _node->_data;}// 箭头运算符,返回数据指针(Ptr可能是T*或const T*)Ptr operator->(){return &_node->_data;}// 前置++,移动到下一个节点Self& operator++(){_node = _node->_next;return *this;}// 后置++,返回旧值并移动到下一个节点Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}// 前置--,移动到前一个节点Self& operator--(){_node = _node->_prev;return *this;}// 后置--,返回旧值并移动到前一个节点Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}// 比较运算符,判断两个迭代器是否指向不同节点bool operator!=(const Self& it) const{return _node != it._node;}// 比较运算符,判断两个迭代器是否指向相同节点bool operator==(const Self& it) const{return _node == it._node;}};// 双向链表模板类template<class T>class list{typedef ListNode<T> Node; // 节点类型别名public:// 迭代器类型定义typedef ListIterator<T, T&, T*> iterator; // 可修改迭代器typedef ListIterator<T, const T&, const T*> const_iterator; // 不可修改迭代器// 获取指向第一个元素的迭代器iterator begin(){return _head->_next;}// 获取尾后迭代器(指向头节点)iterator end(){return _head;}// 获取指向第一个元素的const迭代器const_iterator begin() const{return _head->_next;}// 获取尾后const迭代器const_iterator end() const{return _head;}// 初始化空链表(创建头节点并自环)void empty_init(){_head = new Node; // 创建头节点_head->_next = _head; // 后继指向自己_head->_prev = _head; // 前驱指向自己_size = 0; // 初始化元素个数}// 默认构造函数list(){empty_init();}// 拷贝构造函数(深拷贝)list(const list<T>& lt){empty_init();for (const auto& e : lt) // 遍历源链表元素{push_back(e); // 将元素逐个插入新链表}}// 交换两个链表的内容void swap(list<T>& lt){std::swap(_head, lt._head); // 交换头指针std::swap(_size, lt._size); // 交换元素个数}// 赋值运算符(通过拷贝交换实现)list<T>& operator=(const list<T>& lt){if (this != <) // 防止自赋值{list<T> tmp(lt); // 拷贝构造临时对象swap(tmp); // 交换当前对象与临时对象}return *this; // 返回当前对象}// 清空链表所有元素void clear(){iterator it = begin();while (it != end()) // 遍历删除所有元素{it = erase(it);}}// 析构函数~list(){clear(); // 清空元素delete _head; // 释放头节点_head = nullptr;}// 在链表尾部插入元素void push_back(const T& x){insert(end(), x); // 在end()前插入}// 在链表头部插入元素void push_front(const T& x){insert(begin(), x); // 在begin()前插入}// 删除尾部元素void pop_back(){erase(--end()); // 删除end()的前一个元素}// 删除头部元素void pop_front(){erase(begin()); // 删除第一个元素}// 在pos位置前插入元素iterator insert(iterator pos, const T& val){Node* cur = pos._node; // 当前节点Node* newnode = new Node(val); // 创建新节点Node* prev = cur->_prev; // 前驱节点// 调整指针连接prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++; // 元素计数增加return iterator(newnode); // 返回指向新节点的迭代器}// 删除pos位置的元素iterator erase(iterator pos){assert(pos != end()); // 确保不删除头节点Node* cur = pos._node; // 当前节点Node* next = cur->_next; // 后继节点Node* prev = cur->_prev; // 前驱节点// 调整指针连接prev->_next = next;next->_prev = prev;delete cur; // 释放节点_size--; // 元素计数减少return iterator(next); // 返回后继节点的迭代器}// 获取链表元素个数size_t size() const{return _size;}// 判断链表是否为空bool empty(){return _size == 0;}private:Node* _head; // 头节点指针size_t _size; // 元素个数};
}