这篇小编将以SGI版的list为例进行模拟实现。
目录
一.主体框架
二.具体实现
(一).节点node
(二).主体list(封装,函数)
①对节点封装
②构造函数、拷贝构造函数
③析构函数
④头插头删push/pop_front、尾插尾删push/pop_back
⑤定向插入insert
⑥定向删除erase
⑦赋值
⑧长度size与扩容resize
⑨swap
⑩其他函数empty/front/back/begin/end/rbegin/rend
(三).迭代器iterator
(四).反向迭代器reverse_iterator
三.完整代码
这里小编做了一张图,它足以表明list的大致框架:
先有框架再来一点点具体去了解~
对于节点node而言,我们可以按照c语言中的双向链表节点结构进行创建。
首先node设为一种类,由于list属于泛型编程,我们不知道链表数据类型,因此使用template
大致结构如下:
template
class list_node
{
public:
list_node(T _value = T())//构造函数,使用匿名对象作为缺省
:value(_value)
,next(nullptr)
,prev(nullptr)
{}
T value;//节点数据
list_node* next;//下一个节点
list_node* prev;//上一个节点
};
通过代码我们可以发现,在结构上list_node与c语言的结构体链表节点基本无异。
但是C++特性赋予了链表节点泛型编程的强大功能。
当我们使用list模板时,不可能自己一个个创建节点,再将之连接,否则就与c语言无异了。
因此,需要对节点进一步封装并加上与之配套的函数。由此我们需要创建list类。
其实这很简单,我们只需要在list类内部定义一个node*成员变量作为哨兵位头节点,即node* head。换句话说,其实list内部成员就是链表的头节点。
当我们需要操作链表时,从头节点开始即可。
默认情况下,我们只需要一个头节点即可,即next、prev都指向自己。
如图所示:
typedef list_node Node;
void list_init()//成员函数中不免要使用list初始化,不妨专门定义一个这样的函数
{
head = new Node;
head->next = head;
head->prev = head;
}
List()
{
list_init();
}
当然list构造函数也应该支持按数量和类型构造:
list(int n, const T& value = T())
{
list_init();
for (int i = 0; i < n; ++i)
push_back(value);//后文会有实现
}
除此之外我们发现,官方list还支持按照迭代器的方式构造:
在这里我们可以专门定义一个模板用于表示迭代器类型,这样不管何种迭代器都可以进行构造:
template
List(InputIterator start, InputIterator end)
{
list_init();
while (start != end)
{
push_back(*start);
start++;
}
}
需要注意,list的拷贝构造函数应该是深拷贝
List(const Node& list)
{
Node tmp(list.begin(), list.end());//使用上述构造函数
swap(tmp);//专门定义一个函数用于交换两个list头节点即可
}
对于析构函数而言,最重要的是清空链表节点,以防内存泄漏。
~list()
{
Node* cur = head->next;
while (cur != head)
{
head->next = cur->next;
delete cur;
cur = head->next;
}
head->next = head->prev = head;//将链表闭环
delete head;
head = nullptr;
}
这里的实现思路与普通的带头双向链表无异,具体可以参考这篇博客:带头双向循环链表基础知识归纳
首先看一下官方定义:
我们可知,insert是在选择的迭代器前插入数据,且返回值是插入的第一个元素的迭代器。
那么实现方式就出来了,在pos位置之前插入一个元素即可:
iterator insert(iterator pos, const T& val)
{
Node* pNewNode = new Node(val);
Node* pCur = pos.node;
pNewNode->prev = pCur->prev;
pNewNode->next = pCur;
pNewNode->prev->next = pNewNode;
pCur->prev = pNewNode;
return iterator(pNewNode);
}
当然,批量化插入也可以实现,只需要按所需插入数量while循环调用insert即可,注意返回值是第一次插入的数据的迭代器。
对于参数为迭代器的insert函数只需要从头到尾遍历一遍参数迭代器,将之插入pos之前即可。
从官方定义中我们知道,erase会删除position所代表的迭代器或从first迭代器开始直到last(last不删),返回值是被删除元素的下一个位置的迭代器。
实现方式很简单,我们只需要把pos之前与之后的节点相连即可:
iterator erase(iterator pos)
{
Node* pDel = pos.node;
Node* pRet = pDel->next;
pDel->prev->next = pDel->next;
pDel->next->prev = pDel->prev;
delete pDel;
return iterator(pRet);
}
对于参数为迭代器的同理,只需要把first之前节点与last节点相连,清空原先first与last之间节点,返回last节点。
对于赋值而言,本质是深拷贝,因此,直接在传参时调用拷贝构造深拷贝即可。
Node& operator=(Node l)//参数是实参的拷贝构造
{
swap(l);//调用list成员函数swap
return *this;
}
size的实现与c中一样,遍历链表即可。
size_t size()const
{
Node* cur = head->next;
size_t count = 0;
while (cur != head)
{
count++;
cur = cur->next;
}
return count;
}
扩容需要判断是增容还是缩容。增容push_back,缩容pop_back。
void resize(size_t newsize, const T& data = T())
{
size_t oldsize = size();//成员函数size
if (newsize <= oldsize)//缩容
{
while (newsize < oldsize)
{
pop_back();
oldsize--;
}
}
else//扩容
{
while (oldsize < newsize)
{
push_back(data);
oldsize++;
}
}
}
对于swap函数而言直接交换两个list的头节点指针即可。
void swap(Node& list)
{
std::swap(head, list.head);
}
这一部分比较简单,直接看下文完整代码即可。
对于正向迭代器而言,其使用了类模板而不是单纯使用一个指针实现。
具体原因可以参考这篇博客: 为什么STL中List的实现其迭代器iterator是类模板?
当我们在实现迭代器的时候,需要时刻记得它在表面上看是一个链表节点的指针。因此,其内部一定有一个指向链表节点的指针,还需要支持所有与指针有关的操作:
template
class list_iterator {
typedef list_node Node;
typedef list_iterator Self;
typedef list_iterator iterator;
typedef list_iterator const_iterator;
public://用于reverse_iterator
typedef Ref Ref;
typedef Ptr Ptr;
typedef T T;
public:
list_iterator(Node* _node)
:node(_node)
{}
//支持const_iterator接收iterator
list_iterator(const iterator& it)
: node(it.node)
{}
Ref operator*() {
return node->value;
}
Ptr operator->() {
return &(node->value);
}
bool operator==(const Self& it) const{
return node == it.node;
}
bool operator!=(const Self& it) const{
return node != it.node;
}
Self operator++() {
node = node->next;
return *this;
}
Self operator++(int) {
iterator tmp(node);
node = node->next;
return tmp;
}
Self operator--() {
node = node->prev;
return *this;
}
Self operator--(int) {
iterator tmp(node);
node = node->prev;
return tmp;
}
Node* node;
};
当然,小编在这里利用拷贝构造函数实现了由普通iterator隐式类型转化成const_iterator。
具体原理在此:STL中list如何实现普通迭代器隐式类型转换成const迭代器
SGI版的list对反向迭代器的实现非常巧妙,所谓反向迭代器就是正向的倒序,那么我们可以在内部封装一个正向迭代器,反向++就是正向--,反向--就是正向++。
template
class _reverse_iterator
{
//typename用于明确这是类型名,否则编译器会将之与类成员混淆,
//因为类成员也使用::作用域符访问
typedef typename Iterator::Ref Ref;
typedef typename Iterator::Ptr Ptr;
typedef typename Iterator::T T;
typedef _reverse_iterator Self;
typedef list_iterator iterator;
typedef _reverse_iterator reverse_iterator;
public:
_reverse_iterator(const iterator& it)
:_it(it)
{}
//支持const_reverse_iterator接收reverse_iterator
_reverse_iterator(const reverse_iterator& rit)
: _it(rit._it)
{}
Self operator++(){
_it--;
return *this;
}
Self operator--(){
_it++;
return *this;
}
Ref operator*() {
iterator tmp = _it;
tmp--;
return *tmp;
}
Ref operator->(){
iterator tmp = _it;
tmp--;
return &(*tmp);
}
bool operator==(const Self& rit)const{
return _it != rit._it;
}
bool operator!=(const Self& rit)const{
return _it != rit._it;
}
private:
Iterator _it;//内部封装了一个正向迭代器
};
当然,const_reverse_iterator接收普通反向迭代器也是通过拷贝构造函数实现,其参数是一个普通反向迭代器。 本质还是调用正向迭代器的拷贝构造。
看在小编如此用心的份上,点赞支持一下吧
//链表节点
template
class list_node
{
public:
list_node(T _value = T())
:value(_value)
,next(nullptr)
,prev(nullptr)
{}
T value;
list_node* next;
list_node* prev;
};
//正向迭代器
template
class list_iterator {
typedef list_node Node;
typedef list_iterator Self;
typedef list_iterator iterator;
typedef list_iterator const_iterator;
public://用于reverse_iterator
typedef Ref Ref;
typedef Ptr Ptr;
typedef T T;
public:
list_iterator(Node* _node)
:node(_node)
{}
//支持const_iterator接收iterator
list_iterator(const iterator& it)
: node(it.node)
{}
Ref operator*() {
return node->value;
}
Ptr operator->() {
return &(node->value);
}
bool operator==(const Self& it) const{
return node == it.node;
}
bool operator!=(const Self& it) const{
return node != it.node;
}
Self operator++() {
node = node->next;
return *this;
}
Self operator++(int) {
iterator tmp(node);
node = node->next;
return tmp;
}
Self operator--() {
node = node->prev;
return *this;
}
Self operator--(int) {
iterator tmp(node);
node = node->prev;
return tmp;
}
Node* node;
};
//反向迭代器
template
class _reverse_iterator
{
//typename用于明确这是类型名,否则编译器会将之与类成员混淆,
//因为类成员也使用::作用域符访问
typedef typename Iterator::Ref Ref;
typedef typename Iterator::Ptr Ptr;
typedef typename Iterator::T T;
typedef _reverse_iterator Self;
typedef list_iterator iterator;
typedef _reverse_iterator reverse_iterator;
public:
_reverse_iterator(const iterator& it)
:_it(it)
{}
//支持const_reverse_iterator接收reverse_iterator
_reverse_iterator(const reverse_iterator& rit)
: _it(rit._it)
{}
Self operator++(){
_it--;
return *this;
}
Self operator--(){
_it++;
return *this;
}
Ref operator*() {
iterator tmp = _it;
tmp--;
return *tmp;
}
Ref operator->(){
iterator tmp = _it;
tmp--;
return &(*tmp);
}
bool operator==(const Self& rit)const{
return _it != rit._it;
}
bool operator!=(const Self& rit)const{
return _it != rit._it;
}
private:
Iterator _it;//内部封装了一个正向迭代器
};
//主体
template
class List {
typedef list_node Node;
public:
typedef list_iterator iterator;
typedef list_iterator const_iterator;
typedef _reverse_iterator reverse_iterator;
typedef _reverse_iterator const_reverse_iterator;
void list_init()
{
head = new list_node;
head->next = head;
head->prev = head;
}
List()
{
list_init();
}
template
List(InputIterator start, InputIterator end)
{
list_init();
while (start != end)
{
push_back(*start);
start++;
}
}
List(const Node& list)
{
Node tmp(list.begin(), list.end());
swap(tmp);
}
List(int n, const T& value = T())
{
list_init();
for (int i = 0; i < n; ++i)
push_back(value);
}
Node& operator=(Node l)//参数是实参的拷贝构造
{
swap(l);//调用list成员函数swap
return *this;
}
~List()
{
Node* cur = head->next;
while (cur != head)
{
head->next = cur->next;
delete cur;
cur = head->next;
}
head->next = head->prev = head;//将链表闭环
delete head;
head = nullptr;
}
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);
}
reverse_iterator rbegin() {
return reverse_iterator(end());
}
reverse_iterator rend() {
return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const{
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const{
return const_reverse_iterator(begin());
}
size_t size()const
{
Node* cur = head->next;
size_t count = 0;
while (cur != head)
{
count++;
cur = cur->next;
}
return count;
}
void resize(size_t newsize, const T& data = T())
{
size_t oldsize = size();//成员函数size
if (newsize <= oldsize)//缩容
{
while (newsize < oldsize)
{
pop_back();
oldsize--;
}
}
else//扩容
{
while (oldsize < newsize)
{
push_back(data);
oldsize++;
}
}
}
void push_back(const T& x)
{
Node* newNode = new Node(x);
Node* tail = head->prev;
newNode->prev = tail;
tail->next = newNode;
newNode->next = head;
head->prev = newNode;
}
void push_front(const T& x)
{
Node* newNode = new Node(x);
Node* first = head->next;
newNode->next = first;
first->prev = newNode;
newNode->prev = head;
head->next = newNode;
}
void pop_back() {
assert(head->next != head);
Node* tail = head->prev;
Node* new_tail = tail->prev;
new_tail->next = head;
head->prev = new_tail;
delete tail;
}
void pop_front() {
assert(head->next != head);
Node* front = head->next;
Node* new_front = front->next;
head->next = new_front;
new_front->prev = head;
delete front;
}
iterator insert(iterator pos, const T& val)
{
Node* pNewNode = new Node(val);
Node* pCur = pos.node;
pNewNode->prev = pCur->prev;
pNewNode->next = pCur;
pNewNode->prev->next = pNewNode;
pCur->prev = pNewNode;
return iterator(pNewNode);
}
iterator erase(iterator pos)
{
Node* pDel = pos.node;
Node* pRet = pDel->next;
pDel->prev->next = pDel->next;
pDel->next->prev = pDel->prev;
delete pDel;
return iterator(pRet);
}
void swap(Node& list)
{
std::swap(head, list.head);
}
bool empty()const
{
return head->next == head;
}
T& front()
{
return head->next->value;
}
const T& front()const
{
return head->next->value;
}
T& back()
{
return head->prev->value;
}
const T& back()const
{
return head->prev->value;
}
private:
Node* head;
};
大多数优秀的程序员从事编程工作,不是因为期望获得报酬或得到公众的称赞,而是因为编程是件有趣的事儿——Linus Torvalds
如有错误,敬请斧正