基本结构
首先明确一个问题,list是带头双向循环链表,既然是链表,那么我们就需要定义一个节点的结构体,然后既然是list的模拟实现,就需要定义一个list的类,根据带头双向循环链表的特点,还需要一个头节点,所以list的成员变量中必有一个头节点
下面是基本结构
cpp
namespace bit
{
template<class T>
struct ListNode
{
ListNode<T>* _next;
ListNode<T>* _prev;
T _Date;
ListNode(const T x=T())
{
_next=nullptr;
_prev=nullptr;
_Date=x;
}
};
template<class T>
calss list
{
public:
list()
{
_head=new Node;
_head->_next=_head;
_head->_prev=_head;
}
private:
typedef ListNode<T> Node;
Node* _head;
};
}
上面有一个问题,上面的那个定义节点要用struct,而不用class,原因就是上面的节点里面的任何元素随时都需要访问, 而struct里面的是默认共有的,当然在这里你也可以用calss,只是需要把所有的元素置成公有的
push_back()
向节点的尾部添加一个节点,基本的思路就是找到尾节点,申请新的节点,然后将新的节点连接就行了
cpp
void push_back(const T x)
{
Node* tail = _head->_prev;
Node* newnode = new Node(x);
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
迭代器
今天的这个迭代器会有一点特别,以前在string,vector里面的模拟实现里面,定义一个迭代器都是都是直接typedef一个int*,或者一个char*,让后直接对迭代器进行++或者--,但是,今天的这个是链表,结构在物理上都不是连续的,如何进行++或者--操作,这是不行的,唯一的方法就是对定义一个迭代器的类,然后对++或者--进行操作符的重载
cpp
template<class T>
class ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T> self;
Node* _node;
public:
ListIterator(Node* node)
:_node(node)
{}
self&operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self temp(_node);
_node = _node->_next;
return temp;
}
self&operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self temp(_node);
_node = _node->_prev;
return temp;
}
bool operator==(const self& s)
{
return _node == s._node;
}
bool operator!=(const self& s)
{
return _node != s._node;
}
T& operator*()
{
return _node->_Date;
}
T* operator->()
{
return &(_node->_Date);
}
};
这样就可以对链表进行遍历了,当然了,这里的的begin()和end()函数还没有实现,可以在下面完整代码的list里面去查看
上面代码还有一个问题,你迭代器里面去重载一个--->有什么用
主要是为下面的情况做准备的
上面又又同学问了,啊,->这个操作符得出不是pos的地址吗,你这个怎么好直接就能去的出pos里面的成员变量的,其实上面的写法和被注释掉的代码是一样的,这里编译器是为了增强代码的可读性和美观性才这样省略出这样的,
const迭代器
const迭代器和非const迭代器其实是相差不大的,有人可能认为,在iterator前面加上一个const让他变成const iterator不就成为const迭代器了吗,那你就大错特错了
const迭代器是什么?是迭代器里面指向的内容不能被修改,直接加const不久变成迭代器不能被修改了,这里其实代码和非const迭代器是差不多的,唯一有一个地方需要改的就是限制解引用的返回值,让他变成const
cpp
template<class T>
class ListConstIterator
{
typedef ListNode<T> Node;
typedef ListConstIterator<T> self;
Node* _node;
public:
ListConstIterator(Node* node)
:_node(node)
{}
self&operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self temp(_node);
_node = _node->_next;
return temp;
}
bool operator==(const self& s)
{
return _node == s._node;
}
bool operator!=(const self& s) //记得参数里面加const,如果不加,会出现权限的放大问题
{
return _node != s._node;
}
const T& operator*() //加const
{
return _node->_Date;
}
const T* operator->() //加const
{
return &(_node->_Date);
}
};
两个迭代器合一块
两个迭代器写一块,那不是太麻烦了一点,能不能写一个模板?答案是可以的
只需要在模板参数里面添加两个参数
然后下面函数的返回值稍微改动一下
最后关键的一步来了
在list定义迭代器里面的时候给不同的模板参数就行了
析构函数
cpp
~list()
{
list<T>::iterator it = begin();
while (it != end())
{
it = erase(it);
}
delete _head;
_head = nullptr;
}
拷贝构造和赋值重载
cpp
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
list<T>& operator=(const list<T> l1)
{
empty_init(); //这里记着要初始化
swap(_head, l1._head);
return *this;
}
上面的赋值运算符重载设计得很巧妙,首先明确的一个问题是要使用传值传参,后面直接交换两个_head的指向,因为传值传参会引发拷贝构造函数,后面把_head的指向给l1的拷贝构造了之后函数结束直接析构,这里就有点像让你干活,最后把你给杀了
cpp
list(const list<T>& l1)
{
empty_init();
for (const auto &e : l1)
{
push_back(e);
}
}
这里记得拷贝构造函数需要使用深拷贝,如果你不写拷贝构造函数得话,编译器生成的默认构造函数默认的是浅拷贝,函数调用结束了之后会发生同一块空间析构两次的问题,导致程序崩溃
写代码时的一个易错点
这里写代码的时候老是出现下面的问题:函数匹配问题,这一般都是由于权限的放大缩小问题
还有就是写范围for的时候尽量写引用,因为范围for实际上时赋值给e,赋值的话会拷贝产生一个临时对象,如果auto是自定义类型的话,拷贝量太大了,效率比较低
常规函数
cpp
void erase(iterator pos) //迭代器失效,因为被释放了
{
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
pos._node = nullptr;
}
void insert(iterator&pos, T x) //迭代器没有失效
{
Node* newnode = new Node(x);
Node* prev = pos._node->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = pos._node;
pos._node->_prev = newnode;
}
void pop_back()
{
Node* cur = _head->_prev;
Node* prev = cur->_prev;
prev->_next = _head;
_head->_prev = prev;
delete cur;
}
完整代码
cpp
#include<iostream>
using namespace std;
namespace bit
{
template<class T>
struct ListNode
{
ListNode<T>* _next;
ListNode<T>* _prev;
T _Date;
ListNode(const T date = T())
:_Date(date)
,_prev(nullptr)
,_next(nullptr)
{
}
};
template<class T,class ref,class ptr>
class ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T,ref,ptr> self;
public:
Node* _node;
public:
ListIterator(Node* node)
:_node(node)
{
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self temp(_node);
_node = _node->_next;
return temp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
bool operator==(const self& s)
{
return _node == s._node;
}
bool operator!=(const self& s)
{
return _node != s._node;
}
ref&operator*()
{
return _node->_Date;
}
ptr operator->()
{
return &(_node->_Date);
}
};
/*template<class T>
class ListConstIterator
{
typedef ListNode<T> Node;
typedef ListConstIterator<T> self;
Node* _node;
public:
ListConstIterator(Node* node)
:_node(node)
{}
self&operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self temp(_node);
_node = _node->_next;
return temp;
}
bool operator==(const self& s)
{
return _node == s._node;
}
bool operator!=(const self& s)
{
return _node != s._node;
}
const T& operator*()
{
return _node->_Date;
}
T* operator->()
{
return &(_node->_Date);
}
};*/
template<class T>
class list
{
public:
typedef ListIterator<T, T&,T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
//typedef ListConstIterator<T> const_iterator;
iterator begin()
{
return iterator(_head->_next);
}
const_iterator begin()const
{
return const_iterator(_head->_next);
}
const_iterator end()const
{
return const_iterator(_head);
}
iterator end()
{
return iterator(_head);
}
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
list<T>& operator=(const list<T> l1)
{
empty_init();
swap(_head, l1._head);
return *this;
}
list()
{
empty_init();
}
void push_back(const T x)
{
Node* tail = _head->_prev;
Node* newnode = new Node(x);
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
iterator erase(iterator pos)
{
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
pos._node = nullptr;
return iterator(next);
}
list(const list<T>& l1)
{
empty_init();
for (const auto &e : l1)
{
push_back(e);
}
}
~list()
{
list<T>::iterator it = begin();
while (it != end())
{
it = erase(it);
}
delete _head;
_head = nullptr;
}
bool empty()
{
return _head->_next == _head;
}
void insert(iterator&pos, T x)
{
Node* newnode = new Node(x);
Node* prev = pos._node->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = pos._node;
pos._node->_prev = newnode;
}
void pop_back()
{
Node* cur = _head->_prev;
Node* prev = cur->_prev;
prev->_next = _head;
_head->_prev = prev;
delete cur;
}
private:
typedef ListNode<T> Node;
Node* _head;
};
void test01()
{
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
l1.push_back(40);
l1.push_back(50);
l1.push_back(60);
list<int>::iterator it = l1.begin();
while (it != l1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
void func(const list<int>&s)
{
list<int>::const_iterator it = s.begin();
while (it != s.end())
{
//(*it)++;
cout << *it << " ";
it++;
}
}
void test02()
{
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
l1.push_back(40);
func(l1);
}
struct pos
{
int _x;
int _y;
pos(int x=0,int y=0)
:_x(x)
,_y(y)
{}
};
void test03()
{
list<pos> p;
p.push_back(pos(1,2));
p.push_back(pos(3,4));
p.push_back(pos(5,6));
list<pos>::iterator it = p.begin();
//cout << it.operator->()->_x << " " << it.operator->()->_y << endl;
cout << it->_x << " " << it->_y << endl;
}
void test04()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
for (auto &e : l1)
{
cout << e << " ";
}
cout << endl;
list<int> l2 = l1;
for (auto& e : l2)
{
cout << e << " ";
}
cout << endl;
}
void test05()
{
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
l1.push_back(40);
}
}