概念
list是一个双向链表,可高效地进行插入删除元素。包括构造、方法等。
1. stl_list
模板参数
T
- 类型:元素的类型。
- 别名 :成员类型
list::value_type
。Alloc
- 类型:用于定义存储分配模型的分配器对象的类型。默认情况下,使用分配器类模板,它定义了最简单的内存分配模型,与值类型无关。
- 别名 :成员类型
list::allocator_type
。成员类型
value_type
- 定义 :第一个模板参数(
T
)。allocator_type
- 定义 :第二个模板参数(
Alloc
)。- 默认值 :
allocator<value_type>
。reference
- 定义 :
allocator_type::reference
。- 对于默认的分配器 :
value_type&
。const_reference
- 定义 :
allocator_type::const_reference
。- 对于默认的分配器 :
const value_type&
。pointer
- 定义 :
allocator_type::pointer
。- 对于默认的分配器 :
value_type*
。const_pointer
- 定义 :
allocator_type::const_pointer
。- 对于默认的分配器 :
const value_type*
。iterator
- 定义 :到
value_type
的双向迭代器。- 可转换为 :
const_iterator
。const_iterator
- 定义 :到
const value_type
的双向迭代器。reverse_iterator
- 定义 :
reverse_iterator<iterator>
。const_reverse_iterator
- 定义 :
reverse_iterator<const_iterator>
。difference_type
- 定义 :一个有符号整型,等同于
iterator_traits<iterator>::difference_type
。- 通常等同于 :
ptrdiff_t
。size_type
- 定义 :一个无符号整型,可以表示任何
difference_type
的非负值。- 通常等同于 :
size_t
。成员函数
构造函数和析构函数
- constructor :构造
list
(公共成员函数)。- destructor :销毁
list
(公共成员函数)。- operator=:赋值内容(公共成员函数)。
迭代器
- begin:返回指向开头的迭代器(公共成员函数)。
- end:返回指向末尾的迭代器(公共成员函数)。
- rbegin:返回指向逆向开头的迭代器(公共成员函数)。
- rend:返回指向逆向末尾的迭代器(公共成员函数)。
- cbegin:返回指向开头的常量迭代器(公共成员函数)。
- cend:返回指向末尾的常量迭代器(公共成员函数)。
- crbegin:返回指向逆向开头的常量迭代器(公共成员函数)。
- crend:返回指向逆向末尾的常量迭代器(公共成员函数)。
容量
- empty:测试容器是否为空(公共成员函数)。
- size:返回大小(公共成员函数)。
- max_size:返回最大可容纳的大小(公共成员函数)。
元素访问
- front:访问第一个元素(公共成员函数)。
- back:访问最后一个元素(公共成员函数)。
修改器
- assign:赋值新内容到容器(公共成员函数)。
- emplace_front:在开头构造并插入元素(公共成员函数)。
- push_front:在开头插入元素(公共成员函数)。
- pop_front:删除第一个元素(公共成员函数)。
- emplace_back:在末尾构造并插入元素(公共成员函数)。
- push_back:在末尾插入元素(公共成员函数)。
- pop_back:删除最后一个元素(公共成员函数)。
- emplace:构造并插入元素(公共成员函数)。
- insert:插入元素(公共成员函数)。
- erase:擦除元素(公共成员函数)。
- swap:交换内容(公共成员函数)。
- resize:改变大小(公共成员函数)。
- clear:清除内容(公共成员函数)。
操作
- splice :从一个
list
转移元素到另一个list
(公共成员函数)。- remove:移除具有特定值的元素(公共成员函数)。
- remove_if:移除符合条件的元素(公共成员函数模板)。
- unique:移除重复值(公共成员函数)。
- merge:合并有序列表(公共成员函数)。
- sort:对容器内元素排序(公共成员函数)。
- reverse:逆序容器内元素(公共成员函数)。
观察者
- get_allocator:获取分配器(公共成员函数)。
以上是
std::list
类的常见成员函数,这些函数覆盖了创建、访问、修改和操作列表的所有基础功能。
2. list基本结构
1. 节点结构 (
ListNode
)
ListNode
结构体用于表示链表中的每个节点,它包含了数据、前驱指针和后继指针。
_data
:存储节点的数据。_prev
:指向前一个节点。_next
:指向后一个节点。- 构造函数初始化数据并将前驱和后继指针设为
nullptr
。2. 迭代器实现 (
ListIterator
)
ListIterator
类用于遍历链表,封装了节点指针并提供了一些操作符重载方法。
_node
:指向当前迭代器所指向的节点。- 提供了
==
和!=
运算符重载来比较迭代器。- 提供了解引用运算符
*
和成员访问运算符->
。- 前置和后置的
++
和--
运算符重载用于迭代器的移动。3. 链表类 (
list
)
list
类封装了双向链表的操作,包括构造函数、析构函数、插入、删除等操作。
3. 代码实现
3.1 重载list中运算符
cpp
iterator begin()
{
//iterator it(_head->_next);
//return it;
return iterator(_head->_next);
}
const_iterator begin() const // 只读
{
return const_iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator end() const // 只读
{
return const_iterator(_head);
}
3.2 迭代器的封装
因为自定义的类型++不能支持链表遍历,所以我们需要用一个外壳包住它。
3.2.1 自定义两个类
因为有const和非const之分,我们得有两个类 ListIterator 和 ListConstIterator
ListIterator:
cpp
//迭代器实现(封装1)
template <class T>
class ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T> self;
Node* _node;
public:
ListIterator(Node* node) // 构造
:_node(node)
{}
bool operator==(const self& it) // == 运算符重载
{
return _node == it._node;
}
bool operator!=(const self& it) // != 运算符重载
{
return _node != it._node;
}
T& operator*() // 解引用 运算符重载
{
return _node->_data;
}
self& operator++() // 前置++
{
_node = _node->_next;
return *this;
}
self& operator--() // 前置--
{
_node = _node->_prev;
return *this;
}
self operator++(int) // 后置++
{
self tmp(node);
_node = _node->_next;
return tmp;
}
self operator--(int) // 后置--
{
self tmp(node);
_node = _node->_prev;
return tmp;
}
T* operator->() // -> 运算符重载
{
return &_node->_data;
}
};
ListConstIterator:
cpp
//迭代器实现(封装2)
template <class T>
class ListConstIterator
{
typedef ListNode<T> Node;
typedef ListConstIterator<T> self;
Node* _node;
public:
ListConstIterator(Node* node) // 构造:
:_node(node)
{}
bool operator==(const self& it) // == 运算符重载
{
return _node == it._node;
}
bool operator!=(const self& it) // != 运算符重载
{
return _node != it._node;
}
const T& operator*() // 解引用 运算符重载
{
return _node->_data;
}
self& operator++() // 前置++
{
_node = _node->_next;
return *this;
}
self& operator--() // 前置--
{
_node = _node->_prev;
return *this;
}
self operator++(int) // 后置++
{
self tmp(_node);
_node = _node->_next;
return tmp;
}
self operator--(int) // 后置--
{
self tmp(_node);
_node = _node->_prev;
return tmp;
}
const T* operator->() const // -> 运算符重载
{
return &_node->_data;
}
};
这里提及一下:
cpp
void Func(const list<int>& lt2)
{
list<int>::const_iterator it2 = lt2.begin();
//这里注意:对于lt2需要有一处了解
cout << "const_literator: ";
while (it2 != lt2.end())
{
//*it2 += 10;//只可读
cout << *it2 << " ";
++it2;
}
}
对于拥有常性的对象 **lt2.begin()**可以调用非const的成员函数,如 operator++()
cpp
list<int>::const_iterator it2 = ++lt2.begin();
这里我们可以很明显看出来,分开来写两个迭代器类造成了多处的代码冗余,为了使代码更简洁可以使用模板参数规定返回值类型,可以将两个迭代器类合并为一个。
但二者的本质是一样的
3.2.2 使用模板参数实例化两个类
cpp
//迭代器实现(通过模板,给不同参数实现两个类的实例化)
template <class T, class Ref, class Ptr>
class ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T, Ref, Ptr> self;
Node* _node;
public:
ListIterator(Node* node) // 构造
:_node(node)
{}
bool operator==(const self& it) // == 运算符重载
{
return _node == it._node;
}
bool operator!=(const self& it) // != 运算符重载
{
return _node != it._node;
}
Ref 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;
}
Ptr operator->() // -> 运算符重载
{
return &_node->_data;
}
};
3.2.3 最终list中实例化迭代器
cpp
template <class T>
class list
{
typedef ListNode<T> Node; // 结点
public:
//使用自定义类的方式:
//typedef ListIterator<T> iterator;
//typedef ListConstIterator<T> const_iterator;
//使用模版参数:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
iterator begin()
{
//iterator it(_head->_next);
//return it;
return iterator(_head->_next);
}
const_iterator begin() const // 只读
{
return const_iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator end() const // 只读
{
return const_iterator(_head);
}
//...
};
3.3 操作函数
cpp
void push_back(const T& x) // 尾插
{
Node* newnode = new Node(x);
Node* tail = _head->_prev; // 尾指针指向哨兵节点的前一个结点
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
// 不涉及扩容,insert中没有迭代器失效问题
void insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* newnode = new Node(x);
Node* prev = cur->_prev;
// prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
// erase 后 pos失效,pos指向的结点被释放
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;
return iterator(next);
}
// 尾删
void pop_back()
{
erase(--end());
}
// 头插
void push_front(const T& x)
{
insert(begin(), x);
}
// 头删
void pop_front()
{
erase(begin());
}
在C++中,私有成员只能被声明它们的类和它们的友元访问。之前的迭代器成员变量 _node 是 private
我们有两种解决方案,一是将ListIterator类中的_node设为共有,二是将class 改成结构体
因为方法和变量都是共有,我们不如使用struct,,同样STL库中也是用的struct
4. 最终代码:
cpp
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
template <class T>
struct ListNode
{ // 结点结构
T _data;
ListNode* _prev;
ListNode* _next;
ListNode(const T& data = T()) // 结点默认参数
: _data(data), _prev(nullptr), _next(nullptr) {}
};
namespace bit
{
// list 结点结构
/*
//迭代器实现(封装1)
template <class T>
class ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T> self;
Node* _node;
public:
ListIterator(Node* node) // 构造
:_node(node)
{}
bool operator==(const self& it) // == 运算符重载
{
return _node == it._node;
}
bool operator!=(const self& it) // != 运算符重载
{
return _node != it._node;
}
T& operator*() // 解引用 运算符重载
{
return _node->_data;
}
self& operator++() // 前置++
{
_node = _node->_next;
return *this;
}
self& operator--() // 前置--
{
_node = _node->_prev;
return *this;
}
self operator++(int) // 后置++
{
self tmp(_node);
_node = _node->_next;
return tmp;
}
self operator--(int) // 后置--
{
self tmp(_node);
_node = _node->_prev;
return tmp;
}
T* operator->() // -> 运算符重载
{
return &_node->_data;
}
};
//迭代器实现(封装2)
template <class T>
class ListConstIterator
{
typedef ListNode<T> Node;
typedef ListConstIterator<T> self;
Node* _node;
public:
ListConstIterator(Node* node) // 构造:
:_node(node)
{}
bool operator==(const self& it) // == 运算符重载
{
return _node == it._node;
}
bool operator!=(const self& it) // != 运算符重载
{
return _node != it._node;
}
const T& operator*() // 解引用 运算符重载
{
return _node->_data;
}
self& operator++() // 前置++
{
_node = _node->_next;
return *this;
}
self& operator--() // 前置--
{
_node = _node->_prev;
return *this;
}
self operator++(int) // 后置++
{
self tmp(_node);
_node = _node->_next;
return tmp;
}
self operator--(int) // 后置--
{
self tmp(_node);
_node = _node->_prev;
return tmp;
}
const T* operator->() const // -> 运算符重载
{
return &_node->_data;
}
};
*/
//迭代器实现(通过模板,给不同参数实现两个类的实例化)
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)
{}
bool operator==(const self& it) // == 运算符重载
{
return _node == it._node;
}
bool operator!=(const self& it) // != 运算符重载
{
return _node != it._node;
}
Ref 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;
}
Ptr operator->() // -> 运算符重载
{
return &_node->_data;
}
};
template <class T>
class list
{
typedef ListNode<T> Node; // 结点
public:
//使用自定义类的方式:
//typedef ListIterator<T> iterator;
//typedef ListConstIterator<T> const_iterator;
//使用模版参数:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
iterator begin()
{
//iterator it(_head->_next);
//return it;
return iterator(_head->_next);
}
const_iterator begin() const // 只读
{
return const_iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator end() const // 只读
{
return const_iterator(_head);
}
//头结点初始化 默认构造和拷贝构造都需要,单独提出来,复用减少冗余
void empty_init()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
list() // 默认构造
{
empty_init();
}
list(const list<T>& lt) // 默认构造
{
empty_init();
// 遍历
for (const auto& e : lt)
{
push_back(e);
}
}
//初始化列表
list(initializer_list<T> il) //不需要通过引用传递,因为它已经高效地包装了一个指向常量数组的指针。
{
empty_init();
for (const auto& e : il)
{
push_back(e);
}
}
//赋值拷贝
list<T>& operator=(list<T> lt)
{
swap(_head, lt._head);
return *this;
}
~list() // 析构函数
{
clear();
delete _head;
_head = nullptr;
}
void clear() // 这里注意不能把头结点删除,后面还要复用
{
Node* curr = _head->_next;
while (curr != _head)
{
Node* next = curr->_next;
delete curr;
curr = next;
}
/*
//也可以复用写过的函数
Node* cur = begin();
while (cur != end())
{
delete cur;
next = cur->_next;
}
*/
}
//尾插
void push_back(const T& x) // 尾插
{
Node* newnode = new Node(x);
Node* tail = _head->_prev; // 尾指针指向哨兵节点的前一个结点
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
// 不涉及扩容,insert中没有迭代器失效问题
void insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* newnode = new Node(x);
Node* prev = cur->_prev;
// prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
// erase 后 pos失效,pos指向的结点被释放
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;
return iterator(next);
}
// 尾删
void pop_back()
{
erase(--end());
}
// 头插
void push_front(const T& x)
{
insert(begin(), x);
}
// 头删
void pop_front()
{
erase(begin());
}
// 访问第一个元素
T& front() {
return *begin();
}
// 只读
const T& front() const {
return *begin();
}
// 访问最后一个元素
T& back() {
return *(--end());
}
// 只读
const T& back() const {
return *(--end());
}
// 判空
bool empty() const {
return begin() == end();
}
// 返回链表的元素数量
size_t size() const {
size_t size = 0;
for (auto it = begin(); it != end(); ++it) {
++size;
}
return size;
}
// 交换两个链表的内容
void swap(list<T>& other) {
std::swap(_head, other._head);
}
// 调整链表的大小
void resize(size_t new_size, const T& value = T()) {
size_t current_size = size();
if (new_size < current_size) {
while (current_size > new_size) {
pop_back();
--current_size;
}
}
else if (new_size > current_size) {
while (current_size < new_size) {
push_back(value);
++current_size;
}
}
}
// 用特定值填充链表
void assign(size_t n, const T& value) {
clear();
for (size_t i = 0; i < n; ++i) {
push_back(value);
}
}
private:
Node* _head;
};
}
测试:
cpp
//测试环节:
void Func(const list<int>& lt2)
{
list<int>::const_iterator it2 = lt2.begin();
//这里注意:对于lt2需要有一处了解
cout << "const_literator: ";
while (it2 != lt2.end())
{
//*it2 += 10;//只可读
cout << *it2 << " ";
++it2;
}
}
//迭代器的测试
void test1()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
list<int>::iterator it1 = lt1.begin();
cout << "literator: ";
while (it1 != lt1.end())
{
*it1 += 10;
cout << *it1 << " ";
++it1;
}
cout << endl;
// 支持迭代器就支持了范围for
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
Func(lt1);
cout << endl;
}
//自定义坐标类型
struct pos
{
int _row;
int _col;
pos(int row = 0, int col = 0) //list中的头结点需要默认构造,因此给缺省值
:_row(row)
,_col(col)
{}
};
//两种方式访问成员
void test2()
{
list<pos> lt;
lt.push_back(pos(100, 100));
lt.push_back(pos(200, 200));
lt.push_back(pos(300, 300));
list<pos>::iterator it1 = lt.begin();
while (it1 != lt.end())
{
cout << (*it1)._row << "," << (*it1)._col << " ";
++it1;
}
cout << endl;
list<pos>::iterator it2 = lt.begin();
while (it2 != lt.end())
{
//为了可读性,省略了一个 '->'
//可以写成:
//cout << it2.operator->()->_row << "," << it2.operator->()->_col << " ";
cout << it2->_row << "," << it2->_col << " ";
++it2;
}
cout << endl;
}
// 插入删除的测试
void test3()
{
list<int> lt;
lt.push_back(5);
lt.push_back(6);
lt.push_back(7);
lt.push_back(8);
lt.push_back(9);
for (auto e : lt) { cout << e << " "; }
cout << endl;
lt.push_front(5);
lt.push_front(4);
lt.push_front(3);
lt.push_front(2);
lt.push_front(1);
for (auto e : lt) { cout << e << " "; }
cout << endl;
lt.insert(++lt.begin(), 100);
for (auto e : lt) { cout << e << " "; }
cout << endl;
lt.erase(--lt.end());
for (auto e : lt) { cout << e << " "; }
cout << endl;
}
// 深拷贝测试
void test4()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
for (auto e : lt1) { cout << e << " "; }
cout << endl;
lt1.push_back(4); //这里如果是浅拷贝,lt2仍然能打印出4
list<int> lt2(lt1);
for (auto e : lt2) { cout << e << " "; }
cout << endl;
}
// 简单接口的调用
void test5()
{
list<int> lt1;
list<int> lt2;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
cout << lt1.size() << endl;
cout << lt1.front() << endl;
cout << lt1.back() << endl;
lt2.push_back(4);
lt2.push_back(5);
lt2.push_back(6);
lt1.swap(lt2);
for (auto e : lt1) { cout << e << " "; }
cout << endl;
for (auto e : lt2) { cout << e << " "; }
cout << endl;
lt2.assign(10, 100);
for (auto e : lt2) { cout << e << " "; }
cout << endl;
}
cpp
int main()
{
cout << "test1():" << "\n";
test1();
cout << "test2():" << "\n";
test2();
cout << "test3():" << "\n";
test3();
cout << "test4():" << "\n";
test4();
cout << "test5():" << "\n";
test5();
return 0;
}
输出:
cpp
test1():
literator: 11 12 13 14 15
11 12 13 14 15
const_literator: 11 12 13 14 15
test2():
100,100 200,200 300,300
100,100 200,200 300,300
test3():
5 6 7 8 9
1 2 3 4 5 5 6 7 8 9
1 100 2 3 4 5 5 6 7 8 9
1 100 2 3 4 5 5 6 7 8
test4():
1 2 3
1 2 3 4
test5():
3
1
3
4 5 6
1 2 3
100 100 100 100 100 100 100 100 100 100
至此list中常见的接口函数我们实现完毕了,我们下期再见