目录
1.C++中list的底层结构
2.C++中list容器各个接口函数的使用
3.迭代器的分类
3.1从功能上进行分类
3.2从性质上进行分类
4.list的结构
5.list的模拟实现
5.1默认成员函数(Member functions)
5.1.1构造函数(constructor)
5.1.1.1默认构造函数
5.1.1.2 initializer list构造函数
5.1.1.3拷贝构造(copy)
5.1.2析构函数(destructor)
5.1.3 赋值运算符重载(operator=)
5.2list的迭代器(Iterator)
5.2.1迭代器类模板及类里面的成员变量
5.2.2迭代器类构造函数
5.2.3解引用重载
5.2.3.1 '*' 的重载
5.2.3.2 ‘->’的重载
5.2.4迭代器++/--的实现
5.2.5迭代器的比较运算符
5.2.6 begin()和end()
5.3list的空间操作(Capacity)
5.3.1empty()
5.3.2size()
5.4list的修改操作(Modifiers)
5.4.1swap()
5.4.2clear()
5.4.3push_back()
5.4.4push_front()
5.4.5insert()
5.4.6pop_back()
5.4.7pop_front()
5.4.8erase()
6.参考代码
6.1 list.h
1.C++中list的底层结构
C++中的list容器底层是一个带头双向循环链表。具体结构参考C语言实现双向链表中双向链表的结构。
2.C++中list容器各个接口函数的使用
由于在C++的STL中各容器都封装了比较相似的接口,使用的方式也比较相似,所有这里就不一一介绍了,可以参考C++中string类的使用和C++中vector类的使用,下面直接给的是各个接口的测试代码,感兴趣可以自行进行测试。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <list>
#include <vector>
#include <time.h>
#include <algorithm>
using namespace std;
//Member functions
void test_list1()
{
//1.constructor
//(1)default
list<int> l1;
for (auto& e : l1)
{
cout << e << " ";
}
cout << endl;
//(2)fill
list<int>l2(10);
for (auto& e : l2)
{
cout << e << " ";
}
cout << endl;
list<int>l3(10, 1);
for (auto& e : l3)
{
cout << e << " ";
}
cout << endl;
//(3)range
list<int>l4(10, 2);
list<int>l5(++l4.begin(), --l4.end());
for (auto& e : l5)
{
cout << e << " ";
}
cout << endl;
//(4)copy
list<int>l6(10, 3);
list<int>l7(l6);
for (auto& e : l7)
{
cout << e << " ";
}
cout << endl;
//(5)initializer list
list<int>l8({ 1,2,3,4,5,6 });
list<int>l9 = { 7,8,9,10 };
for (auto& e : l8)
{
cout << e << " ";
}
cout << endl;
for (auto& e : l9)
{
cout << e << " ";
}
cout << endl;
//2.operator=
//(1)copy
list<int> l10 = {1,2,3,4,5,6,7,8,9,10};
list<int> l11;
l11 = l10;
for (auto& e : l11)
{
cout << e << " ";
}
cout << endl;
//(2)initializer list
list<int> l12;
l12 = { 10, 20, 30, 40 };
for (auto& e : l12)
{
cout << e << " ";
}
cout << endl;
}
//Iterators
void test_list2()
{
//1.begin() and end()
list<int> l1 = { 1,2,4,5,6,7 };
list<int>::iterator it1 = l1.begin();
while (it1 != l1.end())
{
cout << *it1 << " ";
it1++;
}
cout << endl;
for (auto& e : l1)
{
cout << e << " ";
}
cout << endl;
list<int>::const_iterator it2 = ++l1.begin();
while (it2 != l1.end())
{
cout << *it2 << " ";
it2++;
}
cout << endl;
for (auto& e : l1)
{
cout << e << " ";
}
cout << endl;
//2. rbegin() and rend()
list<int> l2 = { 10, 20, 30, 40, 50 };
list<int>::reverse_iterator rit = l2.rbegin();
while (rit != l2.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
//3.cbegin() and cend()
list<int> l3 = { 1,2,4,5,6,7 };
list<int>::const_iterator cit = ++l3.cbegin();
while (cit != l3.cend())
{
cout << *cit << " ";
cit++;
}
cout << endl;
for (auto& e : l3)
{
cout << e << " ";
}
cout << endl;
//4.crbegin() and crend() 略
}
//Capacity
void test_list3()
{
//1.empty()
list<int> l1;
list<int> l2 = {1,2,3};
cout << l1.empty() << endl;
cout << l2.empty() << endl;
//2.size()
list<int> l3;
list<int> l4 = { 1,2,3,4,5,6 };
cout << l3.size() << endl;
cout << l4.size() << endl;
//3.max_size()--返回列表容器可以容纳的最大元素数。
}
//Element access
void test_list4()
{
list<int> l1 = { 10,2,3,4,5,6 };
const list<int> l2(10, 1);
//1.front--返回链表首元素的引用
cout << l1.front() << endl;
cout << l2.front() << endl;
//2.back--返回链表最后一个元素的引用
cout << l1.back() << endl;
cout << l2.back() << endl;
}
class A
{
public:
A(int a1 = 1, int a2 = 1)
:_a1(a1)
,_a2(a2)
{}
private:
int _a1;
int _a2;
};
//Modifiers
void test_list5()
{
//1.assign--Assigns new contents to the list container, replacing its current contents, and modifying its size accordingly.
//(1)range
list<int> l1 = { 1,2,3,4,5 };
for (auto& e : l1)
{
cout << e << " ";
}
cout << endl;
cout << l1.size() << endl;
l1.assign(++l1.begin(), --l1.end());
for (auto& e : l1)
{
cout << e << " ";
}
cout << endl;
cout << l1.size() << endl;
//(2)fill
l1.assign(7, 100);
for (auto& e : l1)
{
cout << e << " ";
}
cout << endl;
cout << l1.size() << endl;
//(3)initializer list
l1.assign({ 5,5,5,5,6,6,6,6 });
for (auto& e : l1)
{
cout << e << " ";
}
cout << endl;
cout << l1.size() << endl;
//2.emplace_front() and push_front()
list<int> l2(10, 1);
l2.emplace_front(2);
for (auto& e : l2)
{
cout << e << " ";
}
cout << endl;
l2.push_front(3);
for (auto& e : l2)
{
cout << e << " ";
}
cout << endl;
//3.emplace_back() and push_back()
list<int> l3(10, 2);
l3.emplace_back(3);
for (auto& e : l3)
{
cout << e << " ";
}
cout << endl;
l3.push_back(4);
for (auto& e : l3)
{
cout << e << " ";
}
cout << endl;
//emplace_back()和push_back()的区别
//如果链表中存储的数据是多参数的,则emplace_back中的形参可以写成多参数
list<A> lA;
A aa1 = { 1, 1 };
lA.push_back(aa1); //传对象
lA.push_back(A(2, 2)); //匿名构造
//lA.push_back(2, 2) //push_back()不支持
lA.emplace_back(2, 2); //支持直接传构造A对象的参数
//4.pop_front() and pop_back()
list<int> l4 = { 1,2,3,4 };
for (auto& e : l4)
{
cout << e << " ";
}
cout << endl;
l4.pop_front();
for (auto& e : l4)
{
cout << e << " ";
}
cout << endl;
l4.pop_back();
for (auto& e : l4)
{
cout << e << " ";
}
cout << endl;
//5.emplace()
list<int> l5(10, 0);
l5.emplace(l5.begin(), 1);
l5.emplace(l5.end(), 2);
for (auto& e : l5)
{
cout << e << " ";
}
cout << endl;
//6.insert()
//(1)single element 和 emplace相同
//(2)fill
list<int> l6(5, 0);
l6.insert(++l6.begin(), 7, 1);
for (auto& e : l6)
{
cout << e << " ";
}
cout << endl;
//(3)range
list<int> l7(10, 5);
l7.insert(--l7.end(), l6.begin(), l6.end());
for (auto& e : l7)
{
cout << e << " ";
}
cout << endl;
//(4)initializer list
list<int> l8(10, 3);
l8.insert(++l8.begin(), { 4,4,4,4 });
for (auto& e : l8)
{
cout << e << " ";
}
cout << endl;
//7.erase()
list<int> l9({1,2,3,4,5});
auto it = l9.begin();
++it;
++it;
l9.erase(it);
for (auto e : l9)
{
cout << e << " ";
}
cout << endl;
list<int> l10 = { 1,2,3,4,5 };
l10.erase(++l10.begin(), --l10.end());
for (auto e : l10)
{
cout << e << " ";
}
cout << endl;
//8.swap()
list<int> l11 = { 1,2,3 };
list<int> l12 = { 4,5,6 };
for (auto e : l11)
{
cout << e << " ";
}
cout << endl;
for (auto e : l12)
{
cout << e << " ";
}
cout << endl;
swap(l11, l12);
for (auto e : l11)
{
cout << e << " ";
}
cout << endl;
for (auto e : l12)
{
cout << e << " ";
}
cout << endl;
//9.resize()
list<int> l13(10, 1);
cout << l13.size() << endl;
l13.resize(15, 2); //没有填入参数2,则用0填补
for (auto e : l13)
{
cout << e << " ";
}
cout << endl;
cout << l13.size() << endl;
l13.resize(5);
for (auto e : l13)
{
cout << e << " ";
}
cout << endl;
cout << l13.size() << endl;
//10.clear()
list<int> l14(10, 1);
cout << l14.size() << endl;
l14.clear();
cout << l14.size() << endl;
}
bool single_digit(const int& value)
{
return (value < 10);
}
//Operations
void test_list6()
{
//1.sort()--默认排升序
//降序 - 仿函数
greater<int> gt;
list<int> l1 = { 3,612,7,12,68,43,7,345,234 };
l1.sort();
for (auto& e : l1)
{
cout << e << " ";
}
cout << endl;
l1.sort(gt);
for (auto& e : l1)
{
cout << e << " ";
}
cout << endl;
list<char> l2 = {'x', 's', 's', 's', 'w', 'a'};
l2.sort();
for (auto& e : l2)
{
cout << e << " ";
}
cout << endl;
//2.reverse()
list<int> l3 = { 1,2,3,4,5,6,7,8 };
l3.reverse();
for (auto& e : l3)
{
cout << e << " ";
}
cout << endl;
//3.unique--去重--条件:链表有序
list<int> l4 = { 2,3,4,5,2,5,1,6 };
for (auto& e : l4)
{
cout << e << " ";
}
cout << endl;
l4.unique();
for (auto& e : l4)
{
cout << e << " ";
}
cout << endl;
l4.sort();
l4.unique();
for (auto& e : l4)
{
cout << e << " ";
}
cout << endl;
//4.merge--合并两个链表(按大小顺序合并)--被合并的链表变为空
list<int> l5 = { 1,2,3,4,5 };
list<int> l6 = { 1,2,3,4,5 };
l5.merge(l6);
for (auto& e : l6)
{
cout << e << " ";
}
cout << endl;
for (auto& e : l5)
{
cout << e << " ";
}
cout << endl;
//5.remove--移除一个元素
list<int> l7 = { 1,2,3,4,5,6 };
l7.remove(4);
for (auto& e : l7)
{
cout << e << " ";
}
cout << endl;
//6.remove_if--条件移除,满足传入的条件即移除
list<int> l8 = { 1,2,3,4,5,6,7,8,10,12 };
l8.remove_if(single_digit);
for (auto& e : l8)
{
cout << e << " ";
}
cout << endl;
//6.splice--粘接--被粘接的元素转移到待粘接的链表中--可以在链表自己中进行转移
//(1)entire list
list<int> l9 = {1,2,3,4,5};
list<int> l10 = { 6,7,8,9,10 };
l9.splice(l9.end(),l10);
for (auto& e : l10)
{
cout << e << " ";
}
cout << endl;
for (auto& e : l9)
{
cout << e << " ";
}
cout << endl;
//(2)single element
list<int> l11 = { 1,2,3,4,5 };
list<int> l12 = { 1,2,3,4,5 };
l11.splice(l11.end(), l12, l12.begin());
for (auto& e : l11)
{
cout << e << " ";
}
cout << endl;
for (auto& e : l12)
{
cout << e << " ";
}
cout << endl;
//(3)element range
list<int> l13 = { 1,2,3,4,5 };
list<int> l14 = { 1,2,3,4,5 };
l13.splice(l13.end(), l14, ++l14.begin(), --l14.end());
for (auto& e : l13)
{
cout << e << " ";
}
cout << endl;
for (auto& e : l14)
{
cout << e << " ";
}
cout << endl;
}
//用算法库中的sort进行排序与list中的排序进行对比
void test_list7()
{
srand(time(0));
const int N = 1000000;
list<int> lt1;
vector<int> v;
for (int i = 0; i < N; ++i)
{
auto e = rand() + i;
lt1.push_back(e);
v.push_back(e);
}
int begin1 = clock();
// 排序
sort(v.begin(), v.end());
int end1 = clock();
int begin2 = clock();
lt1.sort();
int end2 = clock();
printf("vector sort:%d\n", end1 - begin1);
printf("list sort:%d\n", end2 - begin2);
}
void test_list8()
{
srand(time(0));
const int N = 1000000;
list<int> lt1;
list<int> lt2;
for (int i = 0; i < N; ++i)
{
auto e = rand() + i;
lt1.push_back(e);
lt2.push_back(e);
}
int begin1 = clock();
// 拷贝vector
vector<int> v(lt2.begin(), lt2.end());
// 排序
sort(v.begin(), v.end());
// 拷贝回lt2
lt2.assign(v.begin(), v.end());
int end1 = clock();
int begin2 = clock();
lt1.sort();
int end2 = clock();
printf("list copy vector sort copy list sort:%d\n", end1 - begin1);
printf("list sort:%d\n", end2 - begin2);
}
int main()
{
//test_list1();
//test_list2();
//test_list3();
//test_list4();
//test_list5();
//test_list6();
//test_list7();
test_list8();
return 0;
}
3.迭代器的分类
3.1从功能上进行分类
(1)iterator -- 普通迭代器
(2)reverse_iterator -- 反向迭代器
(3)const_iterator -- const迭代器
(4)const_reverse_iterator -- const反向迭代器
3.2从性质上进行分类
迭代器从性质上进行分类是由于对应底层的数据结构进行分类的,例如像vector/string这样的容器,底层都是通过数组进行数据的存储,所有它们的迭代器支持随机访问。像list这样的迭代器,底层存储数据的结构是一个一个的节点,并不是一块连续的空间,所有它们不支持随机的空间访问,但是由于list是双向链表,所有它支持++,--进行访问。像forward_list这种单链表的结构,只能支持++并且不支持随机的数据访问。
所以由于底层存储数据的结构不同,从性质上对迭代器进行分类分为下面几类:
(1)单向迭代器(forward iterator):仅仅支持++操作
例如:forward_list/unordered_map/unordered_set
(2)双向迭代器(bidirectional iterator):支持++/--
例如:list/map/set
(3)随机迭代器(random access iterator):支持++/--/+/-
例如:vector/string/deque
注:上述三种迭代器是一种包含关系,双向迭代器是一种特殊的单向迭代器,随机迭代器是一种特殊的双向迭代器或者是一种特殊的单向迭代器。
4.list的结构
链表结构中有一个头节点_head作为双向链表的哨兵位头节点。有一个_size记录链表中节点的个数。
template <class T>
//节点类--节点的结构
struct list_node
{
T _data;
list_node<T>* _next;
list_node<T>* _prev;
//构造函数
list_node(const T& data = T())
:_data(data)
,_next(nullptr)
,_prev(nullptr)
{}
};
template <class T>
//链表类--链表的结构
class list
{
typedef list_node<T> Node;
public:
//...
private:
Node* _head;
size_t _size;
}
5.list的模拟实现
这里声明一下,下列实现的所以接口函数都写在list.h里面,其中进行解释的例子放在test.cpp中,因为接口函数现在list.h中的XiaoC这命名空间中,所以测试的函数在test.cpp中也写在XiaoC这个命名空间中,这样测试函数在进行调用list的接口函数时就不用写命名空间域了。
5.1默认成员函数(Member functions)
5.1.1构造函数(constructor)
5.1.1.1默认构造函数
listn类实例化对象时,里面会有一个哨兵位的头节点,这里写一个创建初始化头节点的函数,用于后面list的构造。
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
默认构造函数即创建一个链表里面只有一个哨兵位头节点
list()
{
empty_init();
}
5.1.1.2 initializer list构造函数
该构造函数即为传入一个initializer_list<T>对象来构造出list,这里的initializer_list<T>对象可以类似理解为数组对象(比如 {1,2,3,4} )。这里调用的push_back()函数在后面进行实现。
list(initializer_list<T> il)
{
empty_init();
for (auto& e : il)
{
push_back(e);
}
}
5.1.1.3拷贝构造(copy)
list(const list<T>& lt)
{
empty_init();
//如果只是不加上上一句代码,这个地方生成的this没有头节点
for (auto& e : lt)
{
push_back(e);
}
}
5.1.2析构函数(destructor)
调用后面实现的clear()函数接口,先对链表中除了头节点的其他节点进行清空,然后再释放头节点,最有将_size置为0。
~list()
{
//先清空链表中的所有节点
clear();
//释放头节点
delete _head;
_head = nullptr;
_size = 0;
}
5.1.3 赋值运算符重载(operator=)
1.传统写法 -- 先清空原来的链表,然后再依次遍历进行尾插。
list<T>& operator=(const list<T>& lt)
{
clear();
for (auto& e : lt)
{
push_back(e);
}
return *this;
}
2.现代写法 -- 因为形参的改变不影响实参,所以通过传值传参传入一个list<T>对象,然后进行交换,这样不改变传入的对象,也进行了赋值运算。swap()函数在下面进行实现。
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
5.2list的迭代器(Iterator)
list的迭代器不同于之前vector和string迭代器,之前vector和string的迭代器都是使用原始指针实现的,迭代器就是指向存储元素的地址,而list的迭代器是指向节点的指针。并且为了实现普通迭代器和const迭代器,list迭代器的实现通过一个类模板进行封装。不然普通迭代器和const迭代器要分别写成两个相似的类,会造成代码的冗余。
//迭代器类 -- 迭代器是节点的指针
template<class T, class Ref, class Ptr>
struct list_iterator
{
//类里面重命名的两个类型
typedef list_node<T> Node;
typedef list_iterator<T, Ref, Ptr> Self;
//类里面的成员变量
Node* _node;
//构造函数+初始化列表
list_iterator(Node* node)
:_node(node)
{}
//解引用运算符重载(如果是原始指针作为迭代器的话,解引用是可以直接拿到其对应的数据的,但是这里的迭代器是一个节点指针,如果要拿到对应节点内部所存储的data需要进行重载)
Ref operator*()
{
return _node->_data;
}
//迭代器模拟的是指针的行为
//_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;
}
};
5.2.1迭代器类模板及类里面的成员变量
template<class T, class Ref, class Ptr>
该模板的第一个类型参数T为存入节点中的数据类型,第二个类型参数是存入节点中数据的类型的引用,如果传入的是T&,即为普通引用,如果传入的是const T&则为const引用。第三个参数是存入节点中数据的指针,如果传入T*即为普通指针,传入const T*即为const指针。所以在list类模板中定义普通迭代器和const迭代器可以这么写:
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
迭代器类模板中的成员变量就是一个节点的指针,只不过这里定义成了公有变量:
Node* _node;
5.2.2迭代器类构造函数
传入一个节点的指针进行构造。
list_iterator(Node* node)
:_node(node)
{}
5.2.3解引用重载
5.2.3.1 '*' 的重载
之前的vector和string迭代器是用原生指针进行模拟的,所以它们的迭代器直接进行解引用就可以得到存储的数据。但是list迭代器是指向节点的指针,直接解引用得到的是节点,所以这里需要进行解引用运算符的重载,使对迭代器解引用之后得到的是节点中存储的数据。
Ref operator*()
{
return _node->_data;
}
5.2.3.2 ‘->’的重载
当list中存储的是自定义对象时,这里我们要访问自定义对象中的成员变量时需要通过重载'->'来进行提取。
Ptr operator->()
{
return &_node->_data;
}
这里重载‘->’返回的存储数据的地址,并不是返回其中的值,这里是一个比较奇怪的地方,通过下列的例子来进行解释,解释的内容放在代码的注释当中了.
#include <iostream>
#include "list.h"
using namespace std;
namespace XiaoC
{
struct AA
{
int _a1 = 1;
int _a2 = 1;
};
void test_list()
{
list<AA> lta;
lta.push_back(AA());
lta.push_back(AA());
lta.push_back(AA());
lta.push_back(AA());
list<AA>::iterator ita = lta.begin();
while (ita != lta.end())
{
//这里用*解引用,返回的是AA类对象
//cout << (*ita)._a1 << ":" << (*ita)._a2 << endl;
cout << ita->_a1 << ":" << ita->_a2 << endl;
//下面是上面行代码的原型,先是调用operator->()返回一个AA*的对象,在用->解引用
//特殊处理:为了可读性省略了一个箭头
//cout << ita.operator->()->_a1 << ":" << ita.operator->()->_a2 << endl;
ita++;
}
cout << endl;
}
};
int main()
{
XiaoC::test_list();
return 0;
}
5.2.4迭代器++/--的实现
这里主要和vector/string迭代器实现的区别是,list实现++/--就是把指向当前节点的指针移到指向后一个/前一个节点并返回迭代器对象.
//迭代器前置++,返回值还是迭代器,是下一个位置的迭代器
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;
}
5.2.5迭代器的比较运算符
这里使用迭代器进行比较大多是为了实现遍历操作,所以这里仅实现了operator!=()和operator==().通过比较节点的指针是否相同进行遍历.
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
5.2.6 begin()和end()
下列代码是在list类模板中对迭代器的使用,这里需要强调的一点是,在begin()函数中,直接返回_head->_next是进行了一个隐式类型转换,因为C++中单参数的类模板支持隐式类型转换.
//用一个模板实现普通迭代器和const迭代器,在这里写的时候写成如下
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
//2.1 begin()
iterator begin()
{
//begin()指向的是链表中第一个有效的数据,即为头节点的下一个节点
//iterator it(_head->_next);
//return it;
//匿名对象
//return iterator(_head->_next);
//隐式类型转换 -- _head->_next是Node*类型,迭代器iterator是list_iterator<T, T&, T*>类型,因为迭代器类里面只有一个Node*类型的成员变量,所有这里可以进行隐式类型转换
return _head->_next;
}
const_iterator begin() const
{
return _head->_next;
}
//2.2 end()
//end是最后一个数据的下一个位置 -- 哨兵位
iterator end()
{
return _head;
}
const_iterator end() const
{
return _head;
}
5.3list的空间操作(Capacity)
5.3.1empty()
bool empty() const
{
return _size == 0;
}
5.3.2size()
size_t size() const
{
return _size;
}
5.4list的修改操作(Modifiers)
在Modifiers中push_back(),push_front(),pop_back(),pop_front()的传统写法以及insert(),erase()的写法仅仅是涉及到指针指向改变的问题,下列就不做过多的解释.
5.4.1swap()
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
5.4.2clear()
这里调用的erase()函数接口,在删除之后返回指向被删除元素的下一个元素的迭代器,所以要通过一个迭代器对象进行接收来进行迭代器的更新.如果不接收,原来指向被删除元素节点的迭代器对象it,当节点被删除后,变成了一个野指针,会导致迭代器失效的问题.
void clear()
{
auto it = begin();
while(it != end())
{
it = erase(it);
}
}
5.4.3push_back()
void push_back(const T& x)
{
//传统写法
Node* newnode = new Node(x);
newnode->_next = _head;
newnode->_prev = _head->_prev;
_head->_prev->_next = newnode;
_head->_prev = newnode;
++_size;
//复用insert()
//insert(end(), x);
}
5.4.4push_front()
//4.4 push_front()
void push_front(const T& x)
{
//传统写法
Node* newnode = new Node(x);
newnode->_next = _head->_next;
newnode->_prev = _head;
_head->_next->_prev = newnode;
_head->_next = newnode;
++_size;
//复用insert()
//insert(begin(), x);
}
5.4.5insert()
iterator insert(iterator pos, const T& x)
{
//在pos位置之前插入数据
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
newnode->_next = cur;
newnode->_prev = prev;
cur->_prev = newnode;
prev->_next = newnode;
++_size;
return newnode; //隐式转换
}
5.4.6pop_back()
void pop_back()
{
//删除尾节点
//传统写法
Node* tail = _head->_prev;
Node* prev = tail->_prev;
prev->_next = _head;
_head->_prev = prev;
delete tail;
tail = nullptr;
//erase(--end());
}
5.4.7pop_front()
void pop_front()
{
//删除头节点
//传统写法
Node* cur = _head->_next;
_head->_next = cur->_next;
cur->_next->_prev = _head;
delete cur;
cur = nullptr;
//erase(begin());
}
5.4.8erase()
//删除pos位置的数据并返回下一个位置的迭代器
iterator erase(iterator pos)
{
assert(pos != end());
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
--_size;
return next;
}
6.参考代码
6.1 list.h
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
namespace XiaoC
{
//节点类
template<class T>
struct list_node
{
T _data;
list_node<T>* _next;
list_node<T>* _prev;
//构造函数
list_node(const T& data = T())
:_data(data)
,_next(nullptr)
,_prev(nullptr)
{}
};
//迭代器类 -- 迭代器是节点的指针
template<class T, class Ref, class Ptr>
struct list_iterator
{
//类里面重命名的两个类型
typedef list_node<T> Node;
typedef list_iterator<T, Ref, Ptr> Self;
//类里面的成员变量
Node* _node;
//构造函数+初始化列表
list_iterator(Node* node)
:_node(node)
{}
//解引用运算符重载(如果是原始指针作为迭代器的话,解引用是可以直接拿到其对应的数据的,但是这里的迭代器是一个节点指针,如果要拿到对应节点内部所存储的data需要进行重载)
Ref operator*()
{
return _node->_data;
}
//迭代器模拟的是指针的行为
//_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;
}
};
//链表类(双向带头循环链表) -- 初始化时有一个哨兵位的头节点
template <class T>
class list
{
typedef list_node<T> Node;
public:
//1. 默认成员函数
//1.1 构造函数
//list类实例化对象时,里面会有一个哨兵位的头节点,这里写一个创建初始化头节点的函数,用于后面list的构造
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
//1.1.1 默认构造函数
list()
{
empty_init();
}
//1.1.2 initializer list
list(initializer_list<T> il)
{
empty_init();
for (auto& e : il)
{
push_back(e);
}
}
//1.1.3 copy
list(const list<T>& lt)
{
empty_init();
//如果只是不加上上一句代码,这个地方生成的this没有头节点
for (auto& e : lt)
{
push_back(e);
}
}
//1.2 析构函数
~list()
{
//先清空链表中的所有节点
clear();
//释放头节点
delete _head;
_head = nullptr;
_size = 0;
}
//1.3 赋值运算符重载
//传统写法
//list<T>& operator=(const list<T>& lt)
//{
// clear();
// for (auto& e : lt)
// {
// push_back(e);
// }
// return *this;
//}
//现代写法
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
//2. Iterator
//用一个模板实现普通迭代器和const迭代器,在这里写的时候写成如下
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
//2.1 begin()
iterator begin()
{
//begin()指向的是链表中第一个有效的数据,即为头节点的下一个节点
//iterator it(_head->_next);
//return it;
//匿名对象
//return iterator(_head->_next);
//隐式类型转换 -- _head->_next是Node*类型,迭代器iterator是list_iterator<T, T&, T*>类型,因为迭代器类里面只有一个Node*类型的成员变量,所有这里可以进行隐式类型转换
return _head->_next;
}
const_iterator begin() const
{
return _head->_next;
}
//2.2 end()
//end是最后一个数据的下一个位置 -- 哨兵位
iterator end()
{
return _head;
}
const_iterator end() const
{
return _head;
}
//3. Capacity
//3.1 empty()
bool empty() const
{
return _size == 0;
}
//3.2 size()
size_t size() const
{
return _size;
}
//4. Modifiers
//4.1 swap()
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
//4.2 clear()
void clear()
{
auto it = begin();
while(it != end())
{
it = erase(it);
}
}
//4.3 push_back()
void push_back(const T& x)
{
//传统写法
Node* newnode = new Node(x);
newnode->_next = _head;
newnode->_prev = _head->_prev;
_head->_prev->_next = newnode;
_head->_prev = newnode;
++_size;
//复用insert()
//insert(end(), x);
}
//4.4 push_front()
void push_front(const T& x)
{
//传统写法
Node* newnode = new Node(x);
newnode->_next = _head->_next;
newnode->_prev = _head;
_head->_next->_prev = newnode;
_head->_next = newnode;
++_size;
//复用insert()
//insert(begin(), x);
}
//4.5 insert()
iterator insert(iterator pos, const T& x)
{
//在pos位置之前插入数据
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
newnode->_next = cur;
newnode->_prev = prev;
cur->_prev = newnode;
prev->_next = newnode;
++_size;
return newnode; //隐式转换
}
//4.6 pop_back()
void pop_back()
{
//删除尾节点
//传统写法
Node* tail = _head->_prev;
Node* prev = tail->_prev;
prev->_next = _head;
_head->_prev = prev;
delete tail;
tail = nullptr;
//erase(--end());
}
//4.7 pop_front()
void pop_front()
{
//删除头节点
//传统写法
Node* cur = _head->_next;
_head->_next = cur->_next;
cur->_next->_prev = _head;
delete cur;
cur = nullptr;
//erase(begin());
}
//4.8 erase()
//删除pos位置的数据并返回下一个位置的迭代器
iterator erase(iterator pos)
{
assert(pos != end());
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
--_size;
return next;
}
private:
//链表里面的结构是一个头节点和size
Node* _head;
size_t _size;
};
template<class Container>
void print_container(const Container& v)
{
auto it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
}