[C++ STL] list类的刨析及简易实现
目录
引言
1. STL list库函数用法概述
1.1 基本构造与初始化
1.2 元素插入与删除
1.3 遍历操作
2. 自我实现list类
2.1 代码结构概述
2.2 节点结构
2.3 迭代器实现
2.4 list类实现
3. 总结
引言
在 C++ 标准模板库(STL)中,list
是一种常用的容器,它实现了双向链表的数据结构。与vector
不同,list
在插入和删除元素时具有较好的性能,尤其是在中间位置进行操作。本文将详细剖析list
类的库函数用法,并结合提供的代码展示如何自我实现一个简单的list
类,重点解析迭代器的实现方法。
1. STL list
库函数用法概述
1.1 基本构造与初始化
STL 的list
类提供了多种构造函数,以下是一些常见的用法:
#include <iostream>
#include <list>int main() {// 默认构造函数std::list<int> lt1;// 初始化列表构造函数std::list<int> lt2 = {1, 2, 3, 4, 5};// 拷贝构造函数std::list<int> lt3(lt2);return 0;
}
1.2 元素插入与删除
list
类提供了丰富的插入和删除元素的方法:
int main() {std::list<int> lt = {1, 2, 3};// 在尾部插入元素lt.push_back(4);// 在头部插入元素lt.push_front(0);// 在指定位置插入元素auto it = std::next(lt.begin(), 2);lt.insert(it, 5);// 删除尾部元素lt.pop_back();// 删除头部元素lt.pop_front();// 删除指定位置元素it = std::next(lt.begin(), 1);lt.erase(it);return 0;
}
1.3 遍历操作
可以使用迭代器或范围for
循环来遍历list
中的元素:
int main() {std::list<int> lt = {1, 2, 3, 4, 5};// 使用迭代器遍历for (auto it = lt.begin(); it != lt.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;// 使用范围for循环遍历for (auto& e : lt) {std::cout << e << " ";}std::cout << std::endl;return 0;
}
2. 自我实现list
类
2.1 代码结构概述
提供的代码实现了一个简单的list
类,包含了节点结构、迭代器和list
类本身。下面将逐步分析代码的各个部分。
2.2 节点结构
template <class T>
struct list_node
{T _data;list_node<T>* _prev;list_node<T>* _next;list_node(const T& data = T()): _data(data), _prev(nullptr), _next(nullptr){}
};
list_node
是一个模板结构体,用于表示双向链表中的节点。每个节点包含一个数据成员_data
,以及指向前一个节点和后一个节点的指针_prev
和_next
。
2.3 迭代器实现
迭代器是list
类的核心部分,它提供了一种统一的方式来遍历链表中的元素。代码中使用了模板参数来实现普通迭代器和常量迭代器的复用:
template <class T, class Ref, class Ptr> // Ref - & Ptr - *
struct list_iterator
{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;Node* _node;list_iterator(Node* node): _node(node){}Ref operator*() {return _node->_data;}Ptr operator->() {return &_node->_data;}Self& operator++() {_node = _node->_next;return *this;}Self& operator--() {_node = _node->_prev;return *this;}Self operator++(int) {Self tmp(*this);_node = _node->_next;return tmp;}Self operator--(int) {Self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& s) const {return _node != s._node;}bool operator==(const Self& s) const {return _node == s._node;}
};
Ref
和Ptr
是模板参数,分别表示引用类型和指针类型。通过这种方式,可以在同一个迭代器模板中实现普通迭代器和常量迭代器。operator*()
和operator->()
重载了解引用和箭头运算符,使得迭代器可以像指针一样使用。operator++()
和operator--()
重载了前置递增和递减运算符,用于移动迭代器的位置。operator++(int)
和operator--(int)
重载了后置递增和递减运算符,它们会返回迭代器的旧值,并移动迭代器的位置。operator!=()
和operator==()
重载了不等于和等于运算符,用于比较两个迭代器是否指向同一个节点。
2.4 list
类实现
template <class T>
class list
{
public:typedef list_node<T> Node;typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;void empty_init() {_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}list() {empty_init();}list(initializer_list<T> li) {empty_init();for (auto& e : li) {push_back(e);}}list(const list<T>& lt) {empty_init();for (auto& e : lt) {push_back(e);}}list<T> operator=(list<T> lt) {swap(lt);return *this;}void swap(list<T>& lt) {std::swap(_head, lt._head);std::swap(_size, lt._size);}~list(){clear();delete _head;_head = nullptr;}iterator begin() {return _head->_next;}iterator end() {return _head;}const_iterator begin() const {return _head->_next;}const_iterator end() const {return _head;}void push_back(const T& x) {insert(end(), x);}void push_front(const T& x) {insert(begin(), x);}void insert(iterator pos, const T& x) {Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;cur->_prev = newnode;newnode->_next = cur;newnode->_prev = prev;_size++;}void pop_back() {erase(--end());}void pop_front() {erase(begin());}void erase(iterator pos) {assert(pos != end());Node* next = pos._node->_next;Node* prev = pos._node->_prev;prev->_next = next;next->_prev = prev;delete pos._node;_size--;}size_t size() const {return _size;}bool empty() const {return _size == 0;}void clear() {Node* prev = _head->_next;Node* cur = prev->_next;while (prev != _head) {delete prev;prev = cur;cur = cur->_next;}_size = 0;}private:Node* _head;size_t _size;
};
empty_init()
函数用于初始化链表的头节点,将其_next
和_prev
指针都指向自身,并将链表的大小初始化为 0。- 构造函数提供了默认构造、初始化列表构造和拷贝构造的实现。
operator=
重载了赋值运算符,使用swap
函数实现了高效的赋值操作。begin()
和end()
函数分别返回指向链表第一个元素和最后一个元素之后位置的迭代器。push_back()
和push_front()
函数用于在链表的尾部和头部插入元素,它们都调用了insert()
函数。insert()
函数在指定位置插入一个新节点,并更新链表的指针和大小。pop_back()
和pop_front()
函数用于删除链表的尾部和头部元素,它们都调用了erase()
函数。erase()
函数删除指定位置的节点,并更新链表的指针和大小。size()
函数返回链表的大小,empty()
函数判断链表是否为空。clear()
函数用于清空链表中的所有元素,并释放内存。
3. 总结
通过本文的分析,我们深入了解了 C++ STL 库的list
类的库函数用法,并结合提供的代码实现了一个简单的list
类。重点解析了迭代器的实现方法,通过模板参数的使用,实现了普通迭代器和const迭代器的复用。自我实现list
类不仅可以帮助我们更好地理解 STL 容器的底层原理,还可以根据实际需求进行定制和扩展。