list列表 - 指南
list 容器的优点
- 高效的插入和删除:由于std::list是基于带头双向循环链表实现的,插入和删除操作复杂度O(1)
- 稳定的迭代器:相对于vector这种,list迭代器失效的情况比较少
- 动态内存管理:std::list可以动态调整大小,根据需要分配和释放内存。能够有效地处理大量元素
但因为是链表结构,空间不连续,占用了更多的内存。在list 的迭代器当中也就没有实现 operator+ 这个函数。
定义私有成员:
private:
Node* _head;
size_t _size;
在官方的List源代码当中,List容器的结点定义在一个结构体当中。
链表节点
template //模板声明
struct list_node
{
list_node* _next; //成员变量
list_node* _prev;
T _val;
list_node(const T& val = T()) //构造函数
:_next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
使用 struct而不是 class,意味着所有成员默认是 public的。
定义正向迭代器
通过模板参数来同时实现 普通迭代器 和 const 迭代器,避免代码冗余。
// T: 数据类型, Ref: 引用类型(T&/const T&), Ptr: 指针类型(T*/const T*)
template
struct __list_iterator //定义一个类来实现迭代起的行为
{
typedef list_node Node;
typedef __list_iterator self; //给自己类型起个别名,简化代码
Node* _node; //节点指针
__list_iterator(Node* node) //构造函数
:_node(node)
{}
// 运算符重载
Ref operator*() // 解引用
{
return _node->_val;
}
Ptr operator->() // 成员访问(编译器会优化掉一个->)
{
return &_node->_val;
}
//前置++
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;
}
};
举例使用

定义反向迭代器
核心思想:
rbegin()对应end()(即最后一个元素的下一个),rend()对应begin()。操作:对反向迭代执行
++操作,实际是调用其内部封装的正向迭代器的--操作。
template
struct __list_reverse_iterator
{
typedef list_node Node;
typedef __list_reverse_iterator self;
Node* _node;
__list_reverse_iterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_val;
}
Ptr operator->()
{
return &_node->_val;
}
// 注意:反向迭代器的++是正向的--
self& operator++()
{
_node = _node->_prev;
return *this;
}
self operator++(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
// 反向迭代器的--是正向的++
self& operator--()
{
_node = _node->_next;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
bool operator!=(const self& it) const
{
return _node != it._node;
}
bool operator==(const self& it) const
{
return _node == it._node;
}
};
定义一个list类
对之前
- list_node结构体 重命名为 Node
- 正向迭代器重命名为 iterator 和 const_iterator
- 反向迭代器重命名为 reverse_iterator 和 const_reverse_iterator
template
class list
{
typedef list_node Node; //重命名
public:
//正向迭代器
typedef __list_iterator iterator;
typedef __list_iterator const_iterator;
//反向迭代器
typedef __list_reverse_iterator reverse_iterator;
typedef __list_reverse_iterator const_reverse_iterator;
......
};
构造函数
void empty_init() //创建一个空指针
{
_head = new Node;
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
list() //构造函数
{
empty_init();
}
析构函数
~list()
{
clear(); //析构函数
delete _head;
_head = nullptr;
}
void clear() //清理
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
_size = 0;
}
拷贝构造函数
// lt2(lt1)
list(const list& lt)
//list(const list& lt)
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
}
push_back
void push_back(const T& x)
{
insert(end(), x);
}
push_front
void push_front(const T& x)
{
insert(begin(), x);
}
pop_back
void pop_back()
{
erase(--end());
}
pop_front
void pop_front()
{
erase(begin());
}
insert
iterator insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_next = cur;
cur->_prev = newnode;
newnode->_prev = prev;
++_size;
return newnode;
}
要返回新插入的元素的位置,防止外部迭代器失效。
erase
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
--_size;
return next;
}
size()
size_t size() { return _size; }
empty
bool empty() const { return _size == 0; }
front()
T& front()
{
assert(_size > 0);
return _head->_next->_val;
}
back()
T& back()
{
assert(_size > 0);
return _head->_prev->_val;
}
赋值运算符重载函数和swap()函数
void swap(list& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
list& operator=(list lt)
//list& operator=(list lt)
{
swap(lt);
return *this;
}
其实上述的 赋值重载运算符函数当中的模板类的类型名可以不用 List<T>,我们知道List<T>是类型名,List是类名,对于模版类,类型名不是 List,而是List<T>,但是如果是在类模板当中写,可以写类名也可以写类型名(下述写法也可以):
list& operator=(list lt)
总代码
#pragma once
#include
#include
using namespace std;
namespace bit
{
template
struct list_node //底层是结构体
{
list_node* _next;
list_node* _prev;
T _val;
list_node(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
// typedef __list_iterator iterator;
// typedef __list_iterator const_iterator;
// T: 数据类型, Ref: 引用类型(T&/const T&), Ptr: 指针类型(T*/const T*)
template
struct __list_iterator
{
typedef list_node Node;
typedef __list_iterator self; // 简化类型名
Node* _node;// 核心:持有一个指向节点的指针
__list_iterator(Node* node)
:_node(node)
{}
// 运算符重载
Ref operator*()// 解引用
{
return _node->_val;
}
Ptr operator->() // 成员访问(编译器会优化掉一个->)
{
return &_node->_val;
}
//前置++
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
struct __list_reverse_iterator
{
typedef list_node Node;
typedef __list_reverse_iterator self;
Node* _node;
__list_reverse_iterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_val;
}
Ptr operator->()
{
return &_node->_val;
}
// 注意:反向迭代器的++是正向的--
self& operator++()
{
_node = _node->_prev;
return *this;
}
self operator++(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
// 反向迭代器的--是正向的++
self& operator--()
{
_node = _node->_next;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
bool operator!=(const self& it) const
{
return _node != it._node;
}
bool operator==(const self& it) const
{
return _node == it._node;
}
};
/*template
struct __list_const_iterator
{
typedef list_node Node;
Node* _node;
__list_const_iterator(Node* node)
:_node(node)
{}
const T& operator*()
{
return _node->_val;
}
__list_const_iterator& operator++()
{
_node = _node->_next;
return *this;
}
__list_const_iterator operator++(int)
{
__list_const_iterator tmp(*this);
_node = _node->_next;
return tmp;
}
bool operator!=(const __list_const_iterator& it)
{
return _node != it._node;
}
bool operator==(const __list_const_iterator& it)
{
return _node == it._node;
}
};*/
template
class list
{
typedef list_node Node; //重命名
public:
//正向迭代器
typedef __list_iterator iterator;
typedef __list_iterator const_iterator;
//反向迭代器
typedef __list_reverse_iterator reverse_iterator;
typedef __list_reverse_iterator const_reverse_iterator;
// 这么设计太冗余了,看看库里面如何设计的
//typedef __list_const_iterator const_iterator;
// 这样设计const迭代器是不行的,因为const迭代器期望指向内容不能修改
// 这样设计是迭代器本身不能修改
// typedef const __list_iterator const_iterator;
// const T* ptr1;
// T* const ptr2;
// 如何设计const迭代器?
iterator begin()
{
//return _head->_next;
return iterator(_head->_next);
}
iterator end()
{
return _head;
//return iterator(_head);
}
const_iterator begin() const
{
//return _head->_next;
return const_iterator(_head->_next);
}
const_iterator end() const
{
return _head;
//return const_iterator(_head);
}
// 反向迭代器的起始是最后一个元素
reverse_iterator rbegin()
{
return reverse_iterator(_head->_prev);
}
// 反向迭代器的结束是头节点(第一个元素的前一个位置)
reverse_iterator rend()
{
return reverse_iterator(_head);
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(_head->_prev);
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(_head);
}
void empty_init() //ok
{
_head = new Node;
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
list() //构造函数
{
empty_init();
}
// lt2(lt1)
list(const list& lt) //拷贝构造函数
//list(const list& lt)
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
}
void swap(list& lt) //swap
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
list& operator=(list lt) //赋值运算符重载
//list& operator=(list lt)
{
swap(lt);
return *this;
}
~list() //析构函数
{
clear();
delete _head;
_head = nullptr;
}
void clear() // ok
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
_size = 0;
}
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());
}
// pos位置之前插入
iterator insert(iterator pos, const T& x) //insert
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_next = cur;
cur->_prev = newnode;
newnode->_prev = prev;
++_size;
return newnode;
}
iterator erase(iterator pos) //erase
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
--_size;
return next;
}
size_t size() //ok
{
/*size_t sz = 0;
iterator it = begin();
while (it != end())
{
++sz;
++it;
}
return sz;*/
return _size;
}
bool empty() const { return _size == 0; }
T& front()
{
assert(_size > 0);
return _head->_next->_val;
}
T& back()
{
assert(_size > 0);
return _head->_prev->_val;
}
private:
Node* _head;
size_t _size;
};
list与vector的区别
| vector | list | |
| 底 层 结 构 | 动态顺序表,是一段连续空间 | 带头结点的双向循环链表 |
| 随 机 访 问 | 支持随机访问,访问某个元素效率O(1) | 不支持随机访问,访问某个元素 效率O(N) |
| 插 入 和 删 除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂 度为O(N),插入时有可能需要增容,增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不 需要搬移元素,时间复杂度为 O(1) |
| 空 间 利 用 率 | 底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高 | 底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低 |
| 迭 代 器 | 原生态指针 | 对原生态指针(节点指针)进行封装 |
| 迭 代 器 失 效 | 失效情况相对较多 | 失效情况相对较少 |
| 使 用 场 景 | 需要随机访问,不关心插入删除 | 大量插入和删除操作,不关心随 机访问 |

浙公网安备 33010602011771号