list的底层是带头双向循环的链表,它可以存放任意数据,因此需要用模板。它功能上的实现其实很简单,但是迭代器是一个比较重要的点,它不是单纯的一个指针,而是一个自定义类型。
一、list类
list的成员有两个,第一个是自义定类型指针,只不过它的类型是一个经过typedef的结点类型,这个结点类型有三个成员,指向下一个结点的指针和指向上一个结点的指针,和存放的值。第二个是一个整形的size,用来记录当前链表数据个数。
namespace vae
{
template <class T>
//list结点类
struct list_node //struct成员默认是公有,方便访问
{
list_node<T>* _next; //类模板之后类名不是类型
list_node<T>* _prev;
T _val;
//构造函数
list_node(const T& val = T())
:_next(nullptr)
,_prev(nullptr)
,_val(val)
{}
}
//list类
template <class T>
class list
{
typedef list_node<T> Node;
public:
private:
//成员
Node* _head;
size_t size;
}
}
二、迭代器类
list的迭代器可不可以用指针呢?显然是不可以的,vector和string可以用指针做迭代器是因为它们的底层是数组,数组它是一段连续的空间,++就可以到下一个的位置,*解引用就可以得到它的值,它天生就可以做迭代器。
但是这里可不可以呢?list的底层是带头双向循环,它的空间是不连续的,++之后谁知道它到哪个位置去了,*解引用能得到它的值吗?不能,所以运算符重载就应该上场了。
迭代器它的成员是一个结点指针,它有三个模板参数,对照着下面这段代码看就可以理解。
typedef _list_iterator<T,T&,T*> iterator;
typedef _list_iterator<T, const T&,const T*> const_iterator;
第二个模板参数是用来解决const迭代器和普通迭代器的问题,这样避免了代码冗余。
第三个模板参数是给->运算符重载用的,比如说我们可能有这样的场景。
我们可以看到在上面的代码中迭代器是访问不了的,为什么呢?因为这个地方list存放的是一个个A对象,解引用之后拿到的是A这个结构体,如果有流插入流提取运算符重载是可以的,可是没有,因此在这个地方我们只能用.来访问,但是我们知道,迭代器是指针,那我们直接用箭头访问不就可以了吗?因此我们需要重载箭头,
但是我们看箭头的运算符重载,我们会发现一个问题it->调用的是运算符重载,它返回的是一个指针,因此指针还要加一个箭头才能访问_a1,所以应该是这样的it->->_a1,但是编译器进行了优化,这样写实在太难看了。
Ptr operator->()
{
return &_node->_val;
}
list迭代器类完整代码:
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->_val;
}
Ptr operator->()
{
return &_node->_val;
}
Self& operator++() //前置++
{
_node = _node->_next;
return *this;
}
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->_next;
return tmp;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
bool operator==(const Self& it)
{
return _node == it._node;
}
};
三、list类中迭代器
typedef _list_iterator<T,T&,T*> iterator;
typedef _list_iterator<T, const T&,const T*> const_iterator;
//迭代器
iterator begin()
{
//return iterator(_head->next);
return _head->_next; //这个地方是隐式类型的转换,生成一个匿名对象
}
iterator end()
{
return _head;
}
const_iterator begin() const
{
return _head->_next;
}
const_iterator end() const
{
return _head;
}
_head是头结点,因此begin就是返回_head的下一个结点,end就是返回_head。我们来看一下list的迭代器使用。
vae::list<int>::iterator it = lt2.begin();
while (it != lt2.end())
{
cout << *it << endl;
it++;
}
list的迭代器使用虽然看上去和vector和string的迭代器使用一样,但是底层大不一样,这个地方
it = lt2.begin(),这是调用拷贝构造,而下面基本全都是调用我们实现的迭代器类里面的运算符重载。
四、pos位置插入删除
1、插入
iterator insert(iterator pos,const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
_size++;
return newnode;
}
list的插入非常简单,new一个结点然后改变前后结点链接关系,再让size++即可。
2、删除
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
_size--;
return next;
}
删除先保存一下删除结点的前后结点,然后改链接关系,在delete掉pos结点,然后size--,注意要返回当前结点的下一个结点,也就是next结点。
五、尾插头插尾删头删
//尾插
void push_back(const T& x)
{
insert(end(), x);
}
//头插
void push_front(const T& x)
{
insert(begin(), x);
}
//尾删
void pop_back()
{
erase(--end());
}
//头删
void pop_front()
{
erase(begin());
}
这也没什么说的好吧,直接复用插入删除。
六、清理数据和交换函数
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
_size = 0;
}
清理数据直接erase就行,list清数据连带空间也要释放掉。
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
直接用库函数交换就ok。
七、当前数据个数和判空
size_t size()const
{
return _size;
}
bool empty()const
{
return _head->_next == _head;
}
八、赋值运算符重载
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
不解释,和list、string一个道理好吧。
九、构造和拷贝构造和析构
//构造
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
//拷贝构造
list(const list<T>& lt)
{
list();
for (auto& e : lt)
{
push_back(e);
}
}
//析构
~list()
{
clear();
delete _head;
_head = nullptr;
}
这个地方拷贝构造直接调用构造开好空间,然后用迭代器和尾插依次把值插入进去即可。
析构直接复用clear清掉空间,然后把头结点释放在置为空。
十、list模拟完整代码
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>
using namespace std;
#include<assert.h>
namespace vae
{
template<class T>
struct list_node
{
list_node<T>* _next;
list_node<T>* _prev;
T _val;
list_node(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
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->_val;
}
Ptr operator->()
{
return &_node->_val;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self operator++(T)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self operator--(T)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
bool operator==(const Self& it)
{
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;
iterator begin()
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator begin() const
{
return _head->_next;
}
const_iterator end() const
{
return _head;
}
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
list(const list<T>& lt)
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
}
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
_size = 0;
}
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;
_size++;*/
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
iterator insert(iterator pos,const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
_size++;
return newnode;
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
_size--;
return next;
}
size_t size()const
{
return _size;
}
bool empty()const
{
return _head->_next == _head;
}
private:
Node* _head;
size_t _size;
};
void list_test1()
{
list<int> li;
li.push_back(1);
li.push_back(2);
li.push_back(3);
li.push_back(4);
list<int>::iterator it= li.begin();
while (it != li.end())
{
(*it)++;
cout << *it << " ";
++it;
}
cout << endl;
}
void list_test2()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.push_front(6);
lt.push_front(7);
lt.push_front(8);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.pop_back();
lt.pop_front();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list<int>lt2(lt);
for (auto e : lt2)
{
cout << e << " ";
}
cout << endl;
lt2.pop_back();
lt = lt2;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
vae::list<int>::iterator it = lt2.begin();
while (it != lt2.end())
{
cout << *it << endl;
it++;
}
}
}
int main()
{
vae::list_test2();
return 0;
}