要手写 List,先明确其底层结构 ——带头双向循环链表,这是所有接口高效实现的基础

那既然是这样的话,是不是就可以说明list中的私有成员仅仅只需要一个头结点就可以了,也就是哨兵位。
list中的私有成员是一个节点类型,那我们就还需要一个节点结构,这个节点结构中需要包含:
也许会有UU想问:为什么会有这个节点结构?
这是因为每个数据存储在一个单独的结构里面,这个结构不仅存储数据,还有指针指向前一个和后一个节点,所以链表必须要有一个节点的结构!!!
ok,话不多说,直接上代码:
namespace carrot
{
//节点类型
template<class T>
struct list_Node
{
list_Node<T>* _prev;//指向前一个节点的指针
list_Node<T>* _next;//指向后一个节点的指针
T data;//存储数据
};
template<class T>
class list
{
using Node = list_Node<T>;
private:
Node* _head;//头结点(哨兵位)
};
}通过前面数据结构中对双向链表的学习,我们知道我们需要一个空的头结点,那我们就需要构造一个空的头结点:

template<class T>
class list
{
using Node = list_Node<T>;
//构造
//构造一个空的头结点
list()
{
_head = new Node;
_head->_prev = _head;
_head->_next = _head;
}
private:
Node* _head;//头结点(哨兵位)
};通过对list底层的分析,我们已经有了一个空的链表,那我们是不是就可以在链表的尾部进行插入数据的操作了~~~
那我们该怎么插入这个数据呢?
在尾插的操作中,哨兵位没有发生改变。

plist本来指向0x100,有了值之后plist指向0x800。


通过前面的学习,我们可以快速写出代码:

当我们运行上面的代码时,会不会有什么问题呢?

那是不是说明我们没有创建一个新的节点空间,并将数据存储进去,那我们就需要在节点结构中加上构造节点的构造函数:
namespace carrot
{
template<class T>
struct list_Node
{
list_Node<T>* _prev;//指向前一个节点的指针
list_Node<T>* _next;//指向后一个节点的指针
T data;//存储数据
//创建一个节点
list_Node(const T& x)
:_prev(nullptr)
,_next(nullptr)
,data(x)
{}
};
}template<class T>
class list
{
public:
//push_back
void push_back(const T& x)
{
//为x创建一个节点空间
Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
}ok,通过上面的操作,我们就实现了List的一个基本框架,那我们接下来看看list中是如何实现迭代器的
也许会有uu想说:迭代器嘛,不就是给指针重新命名成iterator或者const_iterator,然后直接进行 *迭代器或者++迭代器,不就行了,不是很简单嘛~~
ok,我们知道迭代器的特别之处就在于“ * ”和“++”操作,* 就可以直接取出数据,++ 就是下一个位置的迭代器
但是我们想一想,链表list中的迭代器也可以直接进行 * 和 ++ 操作吗?
那我们该怎么实现这个迭代器呢?
问题根源
*iterator 无法直接访问
++iterator 无法直接跳到下一个节点
ok,我们知道迭代器的主要功能无非就是 * 和 ++ 操作,既然数据都在节点中,*iterator无法直接取出数据,++无法直接取下一个接单的地址,所以我们可以用一个类来封装一个迭代器,然后在这个类中实现 * 和 ++ 的运算符重载,在这个类中通过节点的指针来访问数据和++操作,用一个节点的指针来构造一个迭代器
#include<iostream>
using namespace std;
namespace carrot
{
template<class T>
struct list_Node
{
list_Node<T>* _prev;//指向前一个节点的指针
list_Node<T>* _next;//指向后一个节点的指针
T data;//存储数据
list_Node(const T& x)
:_prev(nullptr)
,_next(nullptr)
,data(x)
{}
};
template<class T>
struct list_iterator
{
using Node = list_Node<T>;
using Self = list_iterator<T>;
//用需要访问的节点的指针给_node初始化,构造一个list_iterator对象出来
list_iterator(Node* node)
:_node(node)
{}
Node* _node;
//*
T& operator*()
{
return _node->data;
}
//++
Self& operator++()
{
_node = _node->_next;
return *this;//*this的类型是list_iterator<T>
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
};
}ok,这样我们就实现了一个普通迭代器,这个迭代器的关键所在就在于:使用一个节点的指针构造一个迭代器对象
通过前面的学习,我们知道begin()是第一个有效数据位置的迭代器,那在链表中第一个有效数据所在的节点就是begin()所在的位置

template<class T>
class list
{
public:
using iterator = list_iterator<T>;
iterator begin()
{
return iterator(_head->_next);
}
}
通过前面的学习,我们知道end()是最后一个有效数据位置的下一个位置的迭代器,那在链表中最后一个有效数据位置的下一个位置所在的节点就是end()所在的位置,也就是“哨兵位”所在的位置

template<class T>
class list
{
public:
using iterator = list_iterator<T>;
iterator end()
{
return iterator(_head);
}
}iterator(_head) ; 和上面 begin() 的解释完全一样!!!
ok,迭代器这块还是有点小难度的,接下来我们接着完善list的相关操作:

inset使用迭代器传位置!!!
template<class T>
class list
{
public:
void insert(iterator pos, const T& x)
{
//为x创建节点空间
Node* newnode = new Node(x);
Node* cur = pos._node;
Node* prev = cur->_prev;
newnode->_next = cur;
newnode->_prev = prev;
prev->_next = newnode;
cur->_prev = newnode;
}
}namespace carrot
{
void testList1()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list<int>::iterator it = lt.begin();
lt.insert(it, 10);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
}

erase使用迭代器传位置!!!
void erase(iterator pos)
{
Node* cur = pos._node;
Node* next = cur->_next;
Node* prev = cur->_prev;
prev->_next = next;
next->_prev = prev;
delete cur;
cur = nullptr;
}如果我们的erase按照上面的代码的话,在调用erase中的迭代器会出现失效的情况,为了解决这个问题,我们应该让erase返回删除位置的后一个位置的迭代器,也就是pos(why?因为在删除的过程中,pos后的位置上的数据挪动到pos位置上)
template<class T>
class list
{
public:
iterator erase(iterator pos)
{
Node* cur = pos._node;
Node* next = cur->_next;
Node* prev = cur->_prev;
prev->_next = next;
next->_prev = prev;
delete cur;
cur = nullptr;
return iterator(next);
}
}erase中的pos的解释和insert一样
ok,有了insert和erase,我们就可以在push_back、push_front、pop_back、pop_front中复用insert和erase。
ok,3,2,1~直接上代码
template<class T>
class list
{
public:
//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());
}namespace carrot
{
void testList2()
{
list<int> lt;
//尾插
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//头插
lt.push_front(0);
lt.push_front(-1);
lt.push_front(-2);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//尾删
lt.pop_back();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//头删
lt.pop_front();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
}//size
size_t size() const
{
size_t n = 0;
for (auto e : *this)
{
++n;
}
return n;
}ok,如果按照上面的写法,感觉有点呆,我们可以在list的私有成员中加上size,每当插入数据时size++,删除数据时size--,即可。
template<class T>
class list
{
public:
//size
size_t size() const
{
return _size;
}
private:
Node* _head;//头结点(哨兵位)
size_t _size;
};将链表中的数据删除,保留头结点
template<class T>
class list
{
public:
void clear()
{
it = begin();
while (it != end())
{
it = erase(it);
}
}
}析构也是将链表中的数据清空,并且要释放头结点,所以析构函数可以复用clear
template<class T>
class list
{
public:
////析构
~list()
{
clear();
delete _head;
_head = nullptr;
_size = 0;
}
//clear
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
_size = 0;
}
}在看之前,我们先来看几个构造
可以使用 { } 括一组数据进行构造

ok,当我们使用上面的代码进行构造的时候,发现有运行报错

在进行插入数据之前,头结点不应该为空。
template<class T>
class list
{
public:
using Node = list_Node<T>;
void init()
{
_head = new Node(T());
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
//构造
//构造一个空的头结点
list()
{
init();
}
//initializer_list
list(initializer_list<T> il)
{
//初始化头节点
init();
for (auto& e : il)
{
push_back(e);
}
}
}template<class T>
class list
{
public:
using Node = list_Node<T>;
void init()
{
_head = new Node(T());
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
//构造
//构造一个空的头结点
list()
{
init();
}
//迭代区间构造
template <class InputIterator>
list(InputIterator first, InputIterator last)
{
init();
while (first != last)
{
push_back(*first);
++first;
}
}
}//n个值的构造
list(size_t n ,const T& val = T())
{
init();
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
list(int n, const T& val = T())
{
init();
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}这里实现了两个版本,防止调用n个val的构造时调用到了 initializer_list 构造
ok,接下来
编译器默认生成的拷贝是浅拷贝,对于List而言,浅拷贝还是会出现析构两次的情况,所以需要自己手动实现一个拷贝构造从而完成深拷贝。
//构造一个头结点(哨兵位)
void init()
{
_head = new Node(T());
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
//构造
//构造一个空的头结点
list()
{
init();
}
//拷贝构造
list(const list<T>& v)
{
init();
for (auto& e : v)
{
push_back(e);
}
}list(list<T>& v)
{
init();
list <T> tmp(v.begin(),v.end());
swap(tmp);
}
void swap(list<T>& v)
{
std::swap(_head, v._head);
std::swap(_size, v._size);
}自己实现一个赋值运算符重载,还是因为浅拷贝的问题,这里就不过与赘述了,直接上代码:
//赋值运算符重载
list<T>& operator=(const list<T>& v)
{
if (this != &v)
{
clear();
for (auto& e : v)
{
push_back(e);
}
}
return *this;
}void swap(list<T>& v)
{
std::swap(_head, v._head);
std::swap(_size, v._size);
}
list<T>& operator=(const list<T> tmp)
{
swap(tmp);
return *this;
}我们先来完善一下普通迭代器
namespace carrot
{
template<class T>
struct list_Node
{
list_Node<T>* _prev;//指向前一个节点的指针
list_Node<T>* _next;//指向后一个节点的指针
T data;//存储数据
list_Node(const T& x)
:_prev(nullptr)
,_next(nullptr)
,data(x)
{}
};
template<class T>
struct list_iterator
{
using Node = list_Node<T>;
using Self = list_iterator<T>;
//用需要访问的节点的指针给_node初始化,构造一个list_iterator对象出来
list_iterator(Node* node)
:_node(node)
{}
Node* _node;
//*
T& operator*()
{
return _node->data;
}
//前置++
Self& operator++()
{
_node = _node->_next;
return *this;//*this的类型是list_iterator<T>
}
//后置++
Self operator++(int)
{
Self tmp(*this);
_node = _ndoe->_next;
return tmp;
}
//前置--
Self& operator--()
{
_node = _node->_prev;
return *this;
}
//后置--
Self operator--(int)
{
Self tmp(*this);
_node = _ndoe->_prev;
return tmp;
}
//!=
bool operator!=(const Self& s) const
{
return _node != s._node;
}
//==
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
}也许会有uu想问:为什么不用写析构、拷贝构造和赋值运算符重载?
这是因为迭代器是借助这个节点的指针去访问链表的数据,但是迭代器销毁后,不销毁对应的节点,节点是归链表管的。这就和我们之前的知识接起来了,不要显示写析构的就不需要显示写拷贝构造!!!
在前面的代码实现中,我们已经实现了普通迭代器,那我们该如何实现const迭代器呢?
我们可以直接利用普通迭代器,然后再写一份const迭代器即可~~~
template<class T>
struct list_const_iterator
{
using Node = list_Node<T>;
using Self = list_const_iterator<T>;
//用需要访问的节点的指针给_node初始化,构造一个list_iterator对象出来
list_const_iterator(Node* node)
:_node(node)
{}
Node* _node;
//*
const T& operator*()
{
return _node->data;
}
//++
Self& operator++()
{
_node = _node->_next;
return *this;//*this的类型是list_iterator<T>
}
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& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};有了const迭代器,我们就可以实现相应的const对象的 begin 和 end 了
using const_iterator = list_const_iterator<T>;
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}但是,我们看到const迭代器和普通迭代器的区别仅仅在于数据能否修改,如果我们按照上面的写法,有点浪费
那我们该怎么写呢?我们接着看~
我们对普通迭代器进行改写,在原本只有一个模板参数的情况下,再加个模板参数Ref

也许会有uu会感到疑惑,为什么这里再加了模板参数,就可以实现普通迭代器和const迭代器了呢?
这是因为Ref可以是 T& 或者 const T&,这就可以将普通迭代器和const迭代器合并在一个类中了。
通过模板参数 Ref 控制返回类型:
T& → 可修改的普通迭代器
const T& → 只读的const迭代器
那么 iterator 和 const_iterator ,我们就可以这样写:

这里实现迭代器的本质还是封装指针,我们无法直接 * 和 ++ 得到相应的数据,那我们就可以通过在类中,通过节点的指针构造一个迭代器,在类中实现 * 和 ++ 运算符重载,就可以实现相应的功能。
#pragma once
#include<iostream>
using namespace std;
namespace carrot
{
template<class T>
struct list_Node
{
list_Node<T>* _prev;//指向前一个节点的指针
list_Node<T>* _next;//指向后一个节点的指针
T data;//存储数据
list_Node(const T& x)
:_prev(nullptr)
,_next(nullptr)
,data(x)
{}
};
template<class T,class Ref>
struct list_iterator
{
using Node = list_Node<T>;
using Self = list_iterator<T,Ref>;
//用需要访问的节点的指针给_node初始化,构造一个list_iterator对象出来
list_iterator(Node* node)
:_node(node)
{}
Node* _node;
//*
Ref operator*()
{
return _node->data;
}
//++
Self& operator++()
{
_node = _node->_next;
return *this;//*this的类型是list_iterator<T>
}
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& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
//template<class T>
//struct list_iterator
//{
// using Node = list_Node<T>;
// using Self = list_iterator<T>;
// //用需要访问的节点的指针给_node初始化,构造一个list_iterator对象出来
// list_iterator(Node* node)
// :_node(node)
// {}
// Node* _node;
// //*
// T& operator*()
// {
// return _node->data;
// }
// //++
// Self& operator++()
// {
// _node = _node->_next;
// return *this;//*this的类型是list_iterator<T>
// }
// 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& s) const
// {
// return _node != s._node;
// }
// bool operator==(const Self& s) const
// {
// return _node == s._node;
// }
//};
//template<class T>
//struct list_const_iterator
//{
// using Node = list_Node<T>;
// using Self = list_const_iterator<T>;
// //用需要访问的节点的指针给_node初始化,构造一个list_iterator对象出来
// list_const_iterator(Node* node)
// :_node(node)
// {}
// Node* _node;
// //*
// const T& operator*()
// {
// return _node->data;
// }
// //++
// Self& operator++()
// {
// _node = _node->_next;
// return *this;//*this的类型是list_iterator<T>
// }
// 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& s) const
// {
// return _node != s._node;
// }
// bool operator==(const Self& s) const
// {
// return _node == s._node;
// }
//};
template<class T>
class list
{
public:
using Node = list_Node<T>;
void init()
{
_head = new Node(T());
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
//构造
//构造一个空的头结点
list()
{
init();
}
//initializer_list
list(initializer_list<T> il)
{
init();
for (auto& e : il)
{
push_back(e);
}
}
//迭代区间构造
template <class InputIterator>
list(InputIterator first, InputIterator last)
{
init();
while (first != last)
{
push_back(*first);
++first;
}
}
//n个值的构造
list(size_t n ,const T& val = T())
{
init();
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
list(int n, const T& val = T())
{
init();
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
//拷贝构造
list(const list<T>& v)
{
init();
for (auto& e : v)
{
push_back(e);
}
}
/*list(list<T>& v)
{
init();
list <T> tmp(v.begin(),v.end());
swap(tmp);
}*/
void swap(const list<T>& v) const
{
std::swap(_head, v._head);
std::swap(_size, v._size);
}
//赋值运算符重载
list<T>& operator=(const list<T>& v)
{
if (this != &v)
{
clear();
for (auto& e : v)
{
push_back(e);
}
}
return *this;
}
/*list<T>& operator=(list<T> tmp)
{
swap(tmp);
return *this;
}*/
////析构
~list()
{
/*it = begin();
while (it != end())
{
it = erase(it);
}*/
clear();
delete _head;
_head = nullptr;
_size = 0;
}
//clear
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
_size = 0;
}
//push_back
//void push_back(const T& x)
//{
// //为x创建一个节点空间
// Node* newnode = new Node(x);
// Node* tail = _head->_prev;
// tail->_next = newnode;
// newnode->_prev = tail;
// newnode->_next = _head;
// _head->_prev = newnode;
//}
/*using iterator = list_iterator<T>;
using const_iterator = list_const_iterator<T>;*/
using iterator = list_iterator<T, T&>;
using const_iterator = list_iterator<T, const T&>;
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
void insert(iterator pos, const T& x)
{
//为x创建节点空间
Node* newnode = new Node(x);
Node* cur = pos._node;
Node* prev = cur->_prev;
newnode->_next = cur;
newnode->_prev = prev;
prev->_next = newnode;
cur->_prev = newnode;
++_size;
}
//erase
//为了解决迭代器失效的问题,erase返回删除的下一个位置的迭代器
/*void erase(iterator pos)
{
Node* cur = pos._node;
Node* next = cur->_next;
Node* prev = cur->_prev;
prev->_next = next;
next->_prev = prev;
delete cur;
cur = nullptr;
}*/
iterator erase(iterator pos)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
cur = nullptr;
--_size;
return iterator(next);
}
//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());
}
//size
size_t size() const
{
/*size_t n = 0;
for (auto e : *this)
{
++n;
}
return n;*/
return _size;
}
private:
Node* _head;//头结点(哨兵位)
size_t _size;
};
}#define _CRT_SECURE_NO_WARNINGS
#include"list.h"
namespace carrot
{
void testList1()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list<int>::iterator it = lt.begin();
lt.insert(it, 10);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list<int>::iterator it1 = lt.begin();
lt.erase(it1);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
void testList2()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
cout << lt.size() << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.push_front(0);
lt.push_front(-1);
lt.push_front(-2);
cout << lt.size() << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.pop_back();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.pop_front();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
cout << lt.size() << endl;
lt.clear();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
void testList3()
{
//list<int> lt;
//lt.push_back(1);
//lt.push_back(2);
//for (auto e : lt)
//{
// cout << e << " ";
//}
//cout << endl;
////lt.clear();
//list<int> lt1(lt);
//for (auto e : lt1)
//{
// cout << e << " ";
//}
//cout << endl;
list<int> lt({ 1,2,3,4,5,6 });
/*for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list<int> lt1(lt.begin(), lt.end());
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;*/
list<int> lt1(lt);
/*for (auto e : lt)
{
cout << e << " ";
}
cout << endl;*/
list<int> lt2(10, 1);
for (auto e : lt2)
{
cout << e << " ";
}
cout << endl;
lt2 = lt1;
for (auto e : lt2)
{
cout << e << " ";
}
cout << endl;
}
}
int main()
{
carrot::testList3();
return 0;
}