目录
一.模拟代码实现
#include<iostream>
#include<list>
#include<vector>
#include<algorithm>
#include<assert.h>
#include<functional>
using namespace std;
namespace rzp
{
//很明显,这个是一个节点的结构
template <class T>
struct list_node
{
list_node<T>* _next;
list_node<T>* _prev;
T _data;
list_node(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _data(val)
{
}
};
//这个对应的是迭代器的应用
//typedef _list_iterator<T, T&, T*> iterator;
//typedef _list_iterator<T, const T&, const T*> const_iterator;
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)
{
}
//拷贝构造、operator=、析构我们不写 编译器默认生成就可以用
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
//很明显,这里是指向节点中的数据
//return &(operator*());
return &_node->_data;
}
//前置++
//++it ->it.operator++(&it)
self& operator++()
{
_node = _node->_next;
return *this;
}
//后置++
//it++ -> it.operator++(&it,0)
//因为后置++ 其中的值并没有改变
//所以这里对应的是传值
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 <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;
//在遍历的
//typedef _list_const_iterator<T> const_iterator;
iterator begin()
{
return iterator(_head->_next);
//return _head->_next;
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator end() const
{
return const_iterator(_head);
}
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
//copy(lt)
list(const list<T>& lt)
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
for (const auto& e : lt)
{
push_back(e);
}
}
//析构函数是整个都要销毁
//对应赋值的用法
//list<T>& operator=(const list<T>& lt)
//{
// if (this != <)
// {
// clear();
// for (const auto& e : lt)
// {
// push_back(e);
// }
// }
// return *this;
//}
list<T>& operator=(list<T> lt)
{
swap(_head, lt._head);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
//而clear则只是销毁掉有效数据
void clear()
{
iterator it = begin();
while (it != end())
{
erase(it++);
}
}
//头节点并不会被销毁
//链表的拷贝和构造函数
void push_back(const T& x)
{
//Node* tail = _head->_prev;
//Node* newnode = new Node(x);
_head tail newnode
//tail->_next = newnode;
//newnode->_prev = tail;
//newnode->_next = _head;
//_head->_prev = newnode;
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
void insert(iterator pos, const T& x)
{
assert(pos._node);
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
//prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
//关键还是它们之间的链接关系
iterator erase(iterator pos)
{
assert(pos._node);
assert(pos != end());
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
delete pos._node;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
//iterator erase(iterator pos)
bool empty()
{
return begin() == end();
}
//对应的size()结构
size_t size()
{
size_t sz = 0;
iterator it = begin();
while (it != end())
{
++sz;
++it;
}
return sz;
}
private:
Node* _head;
};
}
二.代码具体分析
2.1 节点类的模拟实现
这个节点其实就是一个双向带头循环的链表结构,代码实现如下:
template <class T>
struct list_node
{
list_node<T>* _next;
list_node<T>* _prev;
T _data;
list_node(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _data(val)
{
}
};
注意:当构造节点并没有传递数据的时候,则默认以缺省参数为值传入数据.
2.2 迭代器类的模拟实现
//这个对应的是迭代器的应用
//typedef _list_iterator<T, T&, T*> iterator;
//typedef _list_iterator<T, const T&, const T*> const_iterator;
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)
{
}
//拷贝构造、operator=、析构我们不写 编译器默认生成就可以用
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
//很明显,这里是指向节点中的数据
//return &(operator*());
return &_node->_data;
}
//前置++
//++it ->it.operator++(&it)
self& operator++()
{
_node = _node->_next;
return *this;
}
//后置++
//it++ -> it.operator++(&it,0)
//因为后置++ 其中的值并没有改变
//所以这里对应的是传值
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;
}
};
对于list来说,其各个结点在内存当中的位置是随机的,并不是连续的,我们不能仅通过结点指针的自增、自减以及解引用等操作对相应结点的数据进行操作。迭代器让使用者可以不必关心容器的底层实现,可以用简单统一的方式对容器内的数据进行访问。
其实就是将在vector和string类的节点指针迭代器进行了封装,并对其各种运算符进行了重载,使得迭代器的各种操作和指针一致.
template<class T, class Ref, class Ptr>
这里:Ref代表引用类型,Ptr代表指针类型.
若该迭代器不设计三个模板参数,那么就不能很好的进行区分普通迭代器和const迭代器.
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
很明显,普通迭代器是可以对节点数据进行改变的,const迭代器限制了节点数据的改变.
2.2.1 迭代器构造函数
_list_iterator(Node* node)
:_node(node)
{
}
2.2.2 ++运算符的重载
//前置++
//++it ->it.operator++(&it)
self& operator++()
{
_node = _node->_next;
return *this;
}
//后置++
//it++ -> it.operator++(&it,0)
//因为后置++ 其中的值并没有改变
//所以这里对应的是传值
self operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
其实.这里的前置++和后置++,这里和vector中的类型极其相似,前置++等同于传引用,因为要改变值;后置++则等同于传值,创建一个临时变量tmp,将节点中的_node改变之后,传出去的是tmp这个临时变量.
2.2.3 --运算符的重载
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
--运算符的重载和++运算符的重载极其的类似.
2.2.4 !=和==运算符的重载
//这块地方的重载也的确是用节点的指针进行比较
bool operator!=(const self& it) const
{
return _node != it._node;
}
bool operator==(const self& it) const
{
return _node == it._node;
}
这里就是对节点的_node进行比较即可,_node中的_prev和_next和_data进行比较.
2.2.5 *运算符的重载
Ref operator*()
{
return _node->_data;
}
*运算符得到该位置的数据内容,并且返回引用.
2.2.6 ->运算符的重载
对于->运算符的重载,我们直接返回结点当中所存储数据的地址即可。
Ptr operator->()
{
//很明显,这里是指向节点中的数据
//return &(operator*());
return &_node->_data;
}
这里返回的是ptr也就是对应节点的指针.
这里本来是应该有两个->的,第一个箭头是pos ->去调用重载的operator->返回Date* 的指针,第二个箭头是Date* 的指针去访问对象当中的成员变量_year。但是一个地方出现两个箭头,程序的可读性太差了,所以编译器做了特殊识别处理,为了增加程序的可读性,省略了一个箭头。
2.3 list的模拟实现
2.3.1 构造函数
list是一个带头双向循环链表,在构造一个list对象时,申请一个头节点,并让其_prev和_next都指向同一个节点.
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;
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
private:
Node* _head;
}
2.3.2 拷贝构造函数
//copy(lt)
list(const list<T>& lt)
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
for (const auto& e : lt)
{
push_back(e);
}
}
2.3.3 赋值运算符重载函数
list<T>& operator=(list<T> lt)
{
swap(_head, lt._head);
return *this;
}
这里直接利用编译器的机制,通过编译器自动调用list的拷贝构造函数,然后利用swap函数将原容器与该list对象进行交换即可
2.3.4 clear函数
void clear()
{
iterator it = begin();
while (it != end())
{
it=erase(it);
}
}
这个很明显就是清除掉容器当中的数据,调用erase进行清除容器中的内容, erase(it)之后return下一个位置的节点.还有另外一种写法如下:
void clear()
{
iterator it = begin();
while (it != end())
{
erase(it++);
}
}
这个写法其实和上面的写法基本一致,程序首先执行erase(it)的操作,但这时it指向的节点并没有发生改变,然后it++就指向了下一个节点.
2.3.5 析构函数
首先调用clear函数清理容器当中,然后将头节点释放,最后将头指针置空即可.
~list()
{
clear();
delete _head;
_head = nullptr;
}
2.3.6 迭代器相关函数
begin和end:begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。即begin指向首元素,end指向尾后。
对于list这个带头双向循环链表来说:
对应迭代器的封装:
iterator begin()
{
return iterator(_head->_next);
//return _head->_next;
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator end() const
{
return const_iterator(_head);
}
2.3.7 插入、删除函数
insert(前插)
void insert(iterator pos, const T& x)
{
assert(pos._node);
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
//prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
这个主要也就是对应的插入函数之间的关系,建立节点之间的关系.
erase(删除)
iterator erase(iterator pos)
{
assert(pos._node);
assert(pos != end());
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
delete pos._node;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
erase函数返回删除节点的下一个节点_next.
头插、头删、尾插、尾删
这四个函数都可以关联对应的erase和insert函数进行实现:
//链表的拷贝和构造函数
//尾插的函数结构
void push_back(const T& x)
{
//Node* tail = _head->_prev;
//Node* newnode = new Node(x);
_head tail newnode
//tail->_next = newnode;
//newnode->_prev = tail;
//newnode->_next = _head;
//_head->_prev = newnode;
insert(end(), x);
}
//头插
void push_front(const T& x)
{
insert(begin(), x);
}
//尾删
void pop_back()
{
erase(--end());
}
//头删
void pop_front()
{
erase(begin());
}
2.3.8 size()
size函数显示list节点的个数:
size_t size()
{
size_t sz = 0;
iterator it = begin();
while (it != end())
{
++sz;
++it;
}
return sz;
}
.