手把手教你实现 C++ 中的 list 容器
·
链表基础
在开始之前,我们先了解一下什么是链表:
-
链表是一种物理存储单元上非连续、非顺序的存储结构
-
双向链表的每个节点都包含指向前一个节点和后一个节点的指针
-
与数组相比,链表在插入和删除操作上更加高效
实现步骤
1. 节点结构
首先,我们需要定义链表的节点结构:
template<class T>
struct list_node {
T _data; // 存储的数据
list_node<T>* _prev; // 指向前一个节点
list_node<T>* _next; // 指向后一个节点
list_node(const T& value = T())
:_data(value),
_prev(nullptr),
_next(nullptr)
{}
};
这个节点结构包含三个成员:
-
_data:存储的实际数据 -
_prev:指向前一个节点的指针 -
_next:指向后一个节点的指针
构造函数使用默认参数,可以创建带有默认值的节点。
2. 迭代器实现
迭代器是遍历容器的工具,对于链表来说尤为重要:
template<class T, class Ref, class Ptr>
struct list_iterator {
typedef list_node<T> Node;
typedef struct 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++(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;
}
};
这里使用了模板参数 Ref 和 Ptr 来区分普通迭代器和常量迭代器。
3. 链表主体
现在让我们来看链表的主要实现:
template<class T>
class list {
typedef list_node<T> Node;
public:
// 定义迭代器类型
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator < T, const T&, const T* > const_iterator;
private:
Node* _head; // 头节点指针
size_t _size; // 链表大小
};
迭代器相关方法
// 返回指向第一个元素的迭代器
iterator begin() {
return iterator(_head->_next);
}
// 返回指向末尾(头节点)的迭代器
iterator end() {
return iterator(_head);
}
// 常量版本
const_iterator begin() const {
return const_iterator(_head->_next);
}
const_iterator end() const {
return const_iterator(_head);
}
初始化方法
// 空初始化
void empty_init() {
_head = new Node;
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
// 默认构造
list() {
empty_init();
}
这里创建了一个哨兵节点(头节点),它指向自己,形成一个空的循环链表。
拷贝构造和赋值
// 拷贝构造
list(const list<T>& l) {
empty_init();
for (auto e : l) {
push_back(e);
}
}
// 赋值操作符
list<T>& operator=(list<T> l) {
swap(l);
return *this;
}
void swap(list<T>& l) {
std::swap(_head, l._head);
std::swap(_size, l._size);
}
这里使用了"拷贝并交换"技术,这是一种安全且高效的实现方式。
析构函数
~list() {
clear();
delete _head;
}
// 清除所有元素
void clear() {
iterator it = begin();
while (it != end()) {
it = erase(it);
}
}
常用操作
头插和头删:
// 头插
void push_front(const T& x) {
Node* newnode = new Node(x);
Node* tmp = _head->_next;
_head->_next = newnode;
tmp->_prev = newnode;
newnode->_prev = _head;
newnode->_next = tmp;
_size++;
}
// 头删
void pop_front() {
assert(_size > 0);
erase(begin());
}
尾插和尾删:
// 尾插
void push_back(const T& x) {
Node* newnode = new Node(x);
Node* tail = _head->_prev;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
tail->_next = newnode;
_size++;
}
// 尾删
void pop_back() {
assert(_size > 0);
erase(--end());
}
插入和删除:
// 在指定位置插入元素
iterator insert(iterator pos, const T& x) {
Node* newnode = new Node(x);
Node* tmp = pos._node->_prev;
newnode->_prev = tmp;
newnode->_next = pos._node;
pos._node->_prev = newnode;
tmp->_next = newnode;
_size++;
return iterator(newnode);
}
// 删除指定位置的元素
iterator erase(iterator pos) {
assert(pos != end());
Node* next = pos._node->_next;
pos._node->_prev->_next = next;
next->_prev = pos._node->_prev;
delete pos._node;
_size--;
return iterator(next);
}
获取大小:
// 返回链表元素个数
size_t size() const {
return _size;
}
使用示例
让我们看看如何使用这个 list 容器:
#include "My_list.h"
#include <iostream>
int main() {
yzq::list<int> myList;
// 添加元素
myList.push_back(1);
myList.push_back(2);
myList.push_back(3);
myList.push_front(0);
// 遍历输出
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 使用范围for循环
for (const auto& val : myList) {
std::cout << val << " ";
}
std::cout << std::endl;
// 删除元素
myList.pop_back();
myList.pop_front();
std::cout << "Size: " << myList.size() << std::endl;
return 0;
}
更多推荐
所有评论(0)