一:大致结构类型的搭建(构造)
二:插入/删除元素的模拟实现
三:迭代器的三种模板参数
四:默认成员函数的模拟实现(拷贝,赋值,析构)
//////
什么是list:
一:大致结构类型的搭建(构造)
1:由cplusplus知,list的底层是双向链表结构,所以我们需要建立起一个又一个的节点,那么如何实现呢?通过对STL库的源码的学习,大致了解到实现的过程,主要是通过封装,通过封装来封装节点,封装迭代器,同时通过模板使迭代器更加适配更多的类型;下面这张图大概是库中实现的主要结构:
2:构造函数
由STL库知,list的构造函数就是构造出一个哨兵位的头结点,又因为我们将节点封装,所以对节点的构造需要在封装节点的类中进行构造,大致代码如下:
//泛型模板:
template <class T>
struct list_node//封装节点
{
list_node<T>* _prev;
list_node<T>* _next;
T data;
//结点构造函数,const T& x=T()是匿名构造,
list_node(const T& x=T())
:_prev(nullptr)
, _next(nullptr)
, data(x)
{
}
};
template <class T>
class List
{
typedef list_node<T> node;//因为封装节点,list_node<T>这样写太繁琐,typedef重命名;
public:
//初始化,建立一个头结点,
void empty_Init()//因为后面还有地方调用,所以写成一个可调用的函数;
{
_phead = new node;//构造结点,调用封装结点类中的构造函数
_phead->_next = _phead;
_phead->_prev = _phead;
}
List()//构造函数:
{
empty_Init();//初始化,建立一个头结点
}
//使用模板的构造函数
template<class ListInputIterator>
List(ListInputIterator first, ListInputIterator last)
{
empty_Init();//初始化建立头结点;
while (first != last)
{
push_back(*first);//一直尾插;
++first;
}
}
private:
node* _phead;//定义一个头结点
};
二:插入/删除元素的模拟实现
//插入删除是通过迭代器来插入/删除的;
void insert(iterator pos, const T& x = T())//在POS指向的结点位置插入,匿名构造;
{
node* prev = pos._node->_prev;//记录迭代器所指向的结点的前一个;
node* next = pos._node;//记录迭代器所指向的结点;
node* newnode = new node(x);//创建一个新的结点,新结点在封装结点类中构造;
//链接 迭代器指向结点的前一个结点 新结点 迭代器指向的结点
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = next;
next->_prev = newnode;
}
//删除POS指向结点位置的结点
iterator erase(iterator pos)//erase需要返回值,后面的析构需要通过返回值向后遍历,
{
assert(pos != _phead);//哨兵位的头结点不能删除;
node* prev = pos._node->_prev;//记录迭代器所指向的结点的前一个;
node* next = pos._node->_next;//记录迭代器所指向的结点的后一个;
链接 迭代器指向结点的前一个结点 迭代器指向结点的后一个结点
prev->_next = next;
next->_prev = prev;
return iterator(next);//返回删除后的迭代器所指向的结点;
}
那么既然任意地方的插入/删除已经实现,那么头插,头删,尾插,尾删就迎刃而解了,可自己实现也可以直接复用insert/erase,具体代码如下所示:
//尾插:
void push_back(const T& x)
{
//自己链接:
node* ptail = _phead->_prev;
node* newnode = new node(x);
ptail->_next = newnode;
newnode->_prev = ptail;
newnode->_next = _phead;
_phead->_prev = newnode;
//直接复用insert,
//insert(_phead, x);
}
//头插:
void push_front(const T& x)
{
//自己链接:
node* next = _phead->_next;
node* newnode = new node(x);
_phead->_next = newnode;
newnode->_prev = _phead;
newnode->_next = next;
next->_prev = newnode;
//直接复用insert,
//insert(_phead->_next, x);
}
//尾删:
void pop_back()
{
//直接复用erase:
erase(_phead->_prev);
}
//头删:
void pop_fornt()
{
//直接复用erase:
erase(_phead->_next);
}
//////
三:迭代器的三种模板参数
1:正常的正向迭代器:
因为将迭代器封装,所以我们需要在封装的迭代器类中创建一个结点,方便遍历,同时通过对vector,与string的了解,迭代器遍历时需要运算符 “!=” ,“*” ,“++”;所以我们需要在封装的迭代器类中重载这些运算符;具体代码如下:
//运用模板
template <class T>
struct list_iterator
{
typedef list_node<T> node;//因为有封装结点,所以我们也要包含,同时重命名方便书写;
typedef list_iterator<T> self;//重命名方便书写;
node* _node;//定义一个结点,方便迭代器向后遍历;
list_iterator(node* node)//迭代器构造函数:
:_node(node)
{
}
T& operator*()//重载解引用运算符,引用返回,返回的是迭代器指向结点的数据;
{
return _node->data;
}
//前置++:返回++后的结点(也就是下一个结点),
self& operator++()//因为返回的是结点,所以返回值是迭代器;
{
_node = _node->_next;
return *this;
}
//后置++:
self operator++(int)//因为返回的是结点,所以返回值是迭代器;
{
self temp(*this);
_node = _node->_next;
return temp;
}
前置--:返回--后的结点(也就是上一个结点),
self& operator--()//因为返回的是结点,所以返回值是迭代器;
{
_node = _node->_prev;
return *this;
}
//后置--:
self operator--(int)//因为返回的是结点,所以返回值是迭代器;
{
self temp(*this);
_node = _node->_prev;
return temp;
}
self& operator+(const T& x)
{
assert(x >= 0);
int i = 0;
while (i < x)
{
_node = _node->_next;
++i;
}
return *this;
}
bool operator!=(const self& LL)
{
return _node != LL._node;
}
bool operator==(const self& LL)
{
return _node == LL._node;
}
};
template <class T>
class List
{
public:
typedef list_iterator<T> iterator;
iterator begin()
{
return iterator(_phead->_next);
}
iterator end()
{
return iterator(_phead);
}
};
///
2:const迭代器:
当我们没有重载const迭代器,那么当不是实例化对象调用迭代器时,那么将会权限放大的报错,但是这里却出现了以下的情况:
没有重载const迭代器,却可以调用,还能正常运行,不会出现权限放大的报错,同时还可以随意更改迭代器所指向结点的值,这是不符合我们对迭代器的期望,而我们对迭代器的要求是遍历容器,而不是能随意更改容器的值,所以,这是不被允许的,所以我们需要重载一份const迭代器;
//为了解决const 迭代器修改链表内容的问题,我们需要将运算符重载*给const化,防止被其修改;
template <class T>
struct const_list_iterator
{
typedef list_node<T> node;
typedef const_list_iterator<T> self;
node* _node;
const_list_iterator(node* node)
:_node(node)
{
}
const T& operator*()
{
return _node->data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self temp(*this);
_node = _node->_next;
return temp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self temp(*this);
_node = _node->_prev;
return temp;
}
self& operator+(const T& x)
{
assert(x >= 0);
int i = 0;
while (i < x)
{
_node = _node->_next;
++i;
}
return *this;
}
bool operator!=(const self& LL)
{
return _node != LL._node;
}
bool operator==(const self& LL)
{
return _node == LL._node;
}
};
当我们写完重载的const迭代器,我们会发现,const迭代器与普通迭代器的区别就是能不能修改,运算符 ‘*’ 是否被const修饰,同时因为写两份差不多的代码冗余,所以我们增加模板参数解决此类问题;即如下所示:
//增加模板参数ref
template <class T,class ref>
struct list_iterator
{
typedef list_node<T> node;
typedef list_iterator<T,ref> self;
node* _node;
list_iterator(node* node)
:_node(node)
{
}
//返回值是ref,当调用的是普通迭代器,那么返回值就是T&,
//当调用的是const迭代器,那么返回值就是const T&
ref operator*()
{
return _node->data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self temp(*this);
_node = _node->_next;
return temp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self temp(*this);
_node = _node->_prev;
return temp;
}
self& operator+(const T& x)
{
assert(x >= 0);
int i = 0;
while (i < x)
{
_node = _node->_next;
++i;
}
return *this;
}
bool operator!=(const self& LL)
{
return _node != LL._node;
}
bool operator==(const self& LL)
{
return _node == LL._node;
}
};
template <class T>
class List
{
public:
typedef list_iterator<T,T&> iterator;
typedef list_iterator<T,const T&> const_iterator;
iterator begin()
{
return iterator(_phead->_next);
}
iterator end()
{
return iterator(_phead);
}
const_iterator begin()const
{
return const_iterator(_phead->_next);
}
const_iterator end()const
{
return const_iterator(_phead);
}
private:
node* _phead;
};
当我们自定义一个类型时,那么普通迭代器和const迭代器就没办法实现我们需要的功能,就算能,看起来非常的不合理,不是我们印象中的自定义类型访问成员变量的样子,所以我们需要在重载一个“->”迭代器,使其可读性更高;
下面是不重载运算符“->”的写法,可以用,能成功运行,但是我们平常对结构体访问成员变量都是使用->,这里使用的"."访问就很不自然;
//自定义类型,
struct mm
{
//因为自定义类型的成员变量是公有,所以我们可以使用之前写的迭代器,
//如果成员变量是私有,则需要重载流插入与流提取;
int _m1;
int _m2;
//自定义类型初始化:
mm(const int& a = 0, const int& b = 0)
:_m1(a)
, _m2(b)
{
}
};
void test_list3()
{
List<mm> l3;
l3.push_back(mm(10, 20));
l3.push_back(mm(30, 40));
l3.push_back(mm(50, 60));
cout << endl;
//使用普通迭代器/const迭代器时也可以正常运行;
List<mm>::iterator it = l3.begin();
while (it != l3.end())
{
//迭代器本质上是对指针的模拟,此时迭代器指向链表中的结点,对其解引用找到其结点
//又因为是自定义类型,所以一个一个的结点就是一个一个的自定义类型,(*it)=mm;
//那么当访问成员变量时,(*it).找到成员变量;
cout << (*it)._m1 << " " << (*it)._m2 << endl;
++it;
}
}
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)
{
}
ref operator*()
{
return _node->data;
}
//重载运算符‘->’,拿到迭代器指向的节点的地址;其他的运算符与普通/const迭代器一样
ptr operator->()
{
return &_node->data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self temp(*this);
_node = _node->_next;
return temp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self temp(*this);
_node = _node->_prev;
return temp;
}
self& operator+(const T& x)
{
assert(x >= 0);
int i = 0;
while (i < x)
{
_node = _node->_next;
++i;
}
return *this;
}
bool operator!=(const self& LL)
{
return _node != LL._node;
}
bool operator==(const self& LL)
{
return _node == LL._node;
}
};
那么我们就不再会用“(*it).”访问成员变量,而使用我们较为熟悉的“->”来实现访问成员变量;
struct mm
{
int _m1;
int _m2;
mm(const int& a = 0, const int& b = 0)
:_m1(a)
, _m2(b)
{
}
};
//自定义类型,结构体,如何访问结构体内成员
void test_list3()
{
List<mm> l3;
l3.push_back(mm(10, 20));
l3.push_back(mm(30, 40));
l3.push_back(mm(50, 60));
cout << endl;
List<mm>::iterator it = l3.begin();
while (it != l3.end())
{
//it.operator->()找到迭代器指向的结点, ->_m1找到结点(自定义类型)的成员变量;
//cout << it.operator->()->_m1 << " " << it.operator->()->_m2 << endl;
cout << it->_m1 << " " << it->_m2 <<endl;//是编译器优化的结果,编译器将“operator->()->”两个->优化为一个->;
//这两个本质差不多,一个是找到结点(自定义类型)的成员变量的地址然后解引用访问成员变量;
//另一个是解引用找到结点(自定义类型),然后通过“.”来访问成员变量;
//cout << *(&(it)->_m1) << " " << *(&(it)->_m2) << endl;
//cout<<(*it)._m1<<" "<<(*it)._m2<<endl;
++it;
}
}
//////
四:默认成员函数的模拟实现(拷贝,赋值,析构)
void clean()
{
iterator it = begin();
while (it != end())
{
//it=erase(it);//因为调用erase时容易pos迭代器失效。所以需要通过返回值接收pos的位置;
erase(it++);//因为我们重载了后置++,删除的是++之前的值,此时的it是it之后的值;
}
}
clern函数实现后,那么实现析构函数就非常简单,先调用clern函数删除不是头结点的结点,然后再将头结点释放掉,具体代码如下所示:
~List()
{
clern();
delete _phead;
_phead = nullptr;
}
///
//普通版本拷贝构造,一个一个拷贝;
//List(const List<T> LL)
//{
// empty_Init();//调用初始化函数,建立一个头结点;
// for (auto e : LL)
// {
// push_back(e);//一个一个尾插;
// }
//}
void swap(List<T>& temp)
{
std::swap(_phead, temp._phead);
}
//拷贝构造:通过库中swap函数交换:
List(List<T>& LL)
{
empty_Init();
List<T>temp(LL.begin(), LL.end());//模板构造
swap(temp);
}
///
List<T>& operator=(List<T> LL)//不能传引用,否则会改变原链表的值;
{
swap(LL);
return *this;
}
//////
至此,这是我对List类的一些接口的较浅理解,如果有不足之处,请各位大佬指正!
我将我所实现的简单list类放在下方,请各位批评指正!
#include <iostream>
#include <assert.h>
using namespace std;
namespace MKL
{
//泛型模板:
template <class T>
struct list_node//封装节点
{
list_node<T>* _prev;
list_node<T>* _next;
T data;
//构造节点函数,const T& x=T()是匿名构造,
list_node(const T& x = T())
:_prev(nullptr)
, _next(nullptr)
, data(x)
{
}
};
//包含普通迭代器/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)
{
}
//返回值是ref,当调用的是普通迭代器,那么返回值就是T&,
//当调用的是const迭代器,那么返回值就是const T&
ref operator*()
{
return _node->data;
}
// //重载运算符‘->’,拿到迭代器指向的节点的地址;
ptr operator->()
{
return &_node->data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self temp(*this);
_node = _node->_next;
return temp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self temp(*this);
_node = _node->_prev;
return temp;
}
self& operator+(const T& x)
{
assert(x >= 0);
int i = 0;
while (i < x)
{
_node = _node->_next;
++i;
}
return *this;
}
bool operator!=(const self& LL)
{
return _node != LL._node;
}
bool operator==(const self& LL)
{
return _node == LL._node;
}
};
//普通版本迭代器
//template <class T>
//struct list_iterator
//{
// typedef list_node<T> node;
// typedef list_iterator<T> self;
// node* _node;
// list_iterator(node* node)
// :_node(node)
// {
// }
// T& operator*()
// {
// return _node->data;
// }
// self& operator++()
// {
// _node = _node->_next;
// return *this;
// }
// self operator++(int)
// {
// self temp(*this);
// _node = _node->_next;
// return temp;
// }
// self& operator--()
// {
// _node = _node->_prev;
// return *this;
// }
// self operator--(int)
// {
// self temp(*this);
// _node = _node->_prev;
// return temp;
// }
// self& operator+(const T& x)
// {
// assert(x >= 0);
// int i = 0;
// while (i < x)
// {
// _node = _node->_next;
// ++i;
// }
// return *this;
// }
// bool operator!=(const self& LL)
// {
// return _node != LL._node;
// }
// bool operator==(const self& LL)
// {
// return _node == LL._node;
// }
//};
//为了解决const 迭代器修改链表内容的问题,我们需要将运算符重载*给const化,防止被其修改;
//同时因为写两份差不多的代码冗余,所以我们增加模板参数解决此类问题
//const版本迭代器:
/*template <class T>
struct const_list_iterator
{
typedef list_node<T> node;
typedef const_list_iterator<T> self;
node* _node;
const_list_iterator(node* node)
:_node(node)
{
}
//运算符重载*const化
const T& operator*()
{
return _node->data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self temp(*this);
_node = _node->_next;
return temp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self temp(*this);
_node = _node->_prev;
return temp;
}
self& operator+(const T& x)
{
assert(x >= 0);
int i = 0;
while (i < x)
{
_node = _node->_next;
++i;
}
return *this;
}
bool operator!=(const self& LL)
{
return _node != LL._node;
}
bool operator==(const self& LL)
{
return _node == LL._node;
}
};*/
template <class T>
class List
{
typedef list_node<T> node;//因为封装节点,list_node<T>这样写太繁琐,typedef重命名;
public:
/*typedef list_iterator<T> iterator;*/
//typedef const_list_iterator<T> const_iterator;
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
//初始化,建立一个头结点,
void empty_Init()//因为后面还有地方调用,所以写成一个可调用的函数;
{
_phead = new node;
_phead->_next = _phead;
_phead->_prev = _phead;
}
List()//构造函数:
{
empty_Init();//初始化,建立一个头结点
}
//模板构造函数:
template<class ListInputIterator>
List(ListInputIterator first, ListInputIterator last)
{
empty_Init();
while (first != last)
{
push_back(*first);
++first;
}
}
//普通版本拷贝构造,一个一个拷贝;
//List(const List<T> LL)
//{
// empty_Init();
// for (auto e : LL)
// {
// push_back(e);
// }
//}
void swap(List<T>& temp)
{
std::swap(_phead, temp._phead);
}
//拷贝构造:通过库中swap函数交换:
List(List<T>& LL)
{
empty_Init();
List<T>temp(LL.begin(), LL.end());
swap(temp);
}
List<T>& operator=(List<T> LL)//不能传引用,否则会改变原链表的值;
{
swap(LL);
return *this;
}
~List()
{
clern();
delete _phead;
_phead = nullptr;
}
//尾插:
void push_back(const T& x)//1 2 3 4
{
//自己链接
//node* ptail = _phead->_prev;
//node* newnode = new node(x);
//ptail->_next = newnode;
//newnode->_prev = ptail;
//newnode->_next = _phead;
//_phead->_prev = newnode;
//复用insert:
insert(end(), x);
}
//头插:
void push_front(const T& x)//1 2 3 4 5
{
//自己链接
//node* next = _phead->_next;
//node* newnode = new node(x);
//_phead->_next = newnode;
//newnode->_prev = _phead;
//newnode->_next = next;
//next->_prev = newnode;
//复用insert:
insert(begin(), x);
}
//尾删:
void pop_back()
{
//复用erase:
erase(_phead->_prev);
}
//头删,
void pop_fornt()
{
//复用erase:
erase(begin());
}
//任意地方插入:
void insert(iterator pos, const T& x = T())
{
node* prev = pos._node->_prev;
node* next = pos._node;
node* newnode = new node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = next;
next->_prev = newnode;
}
//任意地方删除:
iterator erase(iterator pos)
{
assert(pos != _phead);
node* prev = pos._node->_prev;
node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
//普通迭代器
iterator begin()
{
return iterator(_phead->_next);
}
//普通迭代器
iterator end()
{
return iterator(_phead);
}
//const迭代器
const_iterator begin()const
{
return const_iterator(_phead->_next);
}
//const迭代器
const_iterator end()const
{
return const_iterator(_phead);
}
void clean()
{
iterator it = begin();
while (it != end())
{
//it=erase(it);//因为调用erase时容易pos迭代器失效。所以需要通过返回值接收pos的位置;
erase(it++);//因为我们重载了后置++,删除的是++之前的值,此时的it是it之后的值;
}
}
private:
node* _phead;
};
//构建链表基本结构与普通迭代器,以及任意地方插入与删除:
void test_List1()
{
List<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(5);
List<int>::iterator it = l1.begin();
while (it != l1.end())
{
cout << *(it) << " ";
++it;
}
cout << endl;
l1.push_front(10);
l1.insert(l1.begin() + 2, 20);
l1.erase(l1.begin() + 4);
for (auto e : l1)
{
cout << e << " ";
}
cout << endl;
}
void my_print(List<int>& LL)
{
List<int>::iterator it = LL.begin();
while (it != LL.end())
{
*(it) *= 2;
cout << *(it) << " ";
++it;
}
cout << endl;
}
//const迭代器:
void test_list2()
{
List<int> l2;
l2.push_back(10);
l2.push_back(20);
l2.push_back(30);
l2.push_back(40);
l2.push_back(50);
for (auto e : l2)
{
cout << e << " ";
}
cout << endl;
my_print(l2);
}
struct mm
{
int _m1;
int _m2;
mm(const int& a = 0, const int& b = 0)
:_m1(a)
, _m2(b)
{
}
};
//自定义类型,结构体,如何访问结构体内成员
void test_list3()
{
List<mm> l3;
l3.push_back(mm(10, 20));
l3.push_back(mm(30, 40));
l3.push_back(mm(50, 60));
cout << endl;
List<mm>::iterator it = l3.begin();
while (it != l3.end())
{
//it.operator->()找到迭代器指向的结点, ->_m1找到结点(自定义类型)的成员变量;
//cout << it.operator->()->_m1 << " " << it.operator->()->_m2 << endl;
cout << it->_m1 << " " << it->_m2 <<endl;//是编译器优化的结果,编译器将“operator->()->”两个->优化为一个->;
//这两个本质差不多,一个是找到结点(自定义类型)的成员变量的地址然后解引用访问成员变量;
//另一个是解引用找到结点(自定义类型),然后通过“.”来访问成员变量;
//cout << *(&(it)->_m1) << " " << *(&(it)->_m2) << endl;
//cout<<(*it)._m1<<" "<<(*it)._m2<<endl;
++it;
}
}
//拷贝/赋值/析构函数
void test_list4()
{
List<int> l4;
l4.push_back(10);
l4.push_back(20);
l4.push_back(30);
l4.push_back(40);
l4.push_back(50);
for (auto e : l4)
{
cout << e << " ";
}
cout << endl;
//拷贝构造:
List<int> l5(l4);
for (auto e : l5)
{
cout << e << " ";
}
cout << endl;
for (auto e : l4)
{
cout << e << " ";
}
cout << endl;
//调用赋值运算符:
List<int> l6;
l6 = l4;
for (auto e : l6)
{
cout << e << " ";
}
cout << endl;
for (auto e : l4)
{
cout << e << " ";
}
cout << endl;
}
}