写在前面:相信小伙伴们对C++标准模板库STL中的list都不陌生,对于list的操作,网上也有大量的资料可供参考,但是,对于其底层是怎么实现的却鲜有介绍。list做为一个标准的模板库,其实用性和复杂性都不言而喻,要想通过自己实现和STL中list一致的功能,工作量是极其繁琐并且也没必要。本文和接下来的几篇文章,将通过手动制作一个list类,实现list的常用操作,在做的过程中也会介绍容器的使用方法。这样能让我们大体(只能说大体)了解其底层的实现原理,至于它真正的底层实现,大家可以参考相关书籍。
1.建立一个list类
新建一个Mylist.h头文件,由于STL::list是一个双向链表结构,所以我们也创建一个双向链表类。当然链表的每个节点是必不可少的,所以我们也需要建了一个节点类,由于是双向链表,所以节点应该有两个指针,分别指向前一个和后一个
。如下,这里把class Mylist定义为class ListNode的友元类,以便访问其私有成员。
#ifndef _MY_LIST_H
#define _MY_LIST_H
template <typename T>
class Mylist;
//定义双向链表的结点类
template <typename T>
class ListNode
{
friend class Mylist<T>;//Mylist类为ListNode类的友元,从而可以访问其私有成员
private:
T data;
ListNode<T> * leftlink;
ListNode<T> * rightlink;
ListNode() :leftlink(nullptr), rightlink(nullptr){};
ListNode(T _data) :data(_data), leftlink(nullptr), rightlink(nullptr){}
};
//定义双向链表类
template <typename T>
class Mylist
{
public:
Mylist();
~Mylist();
private:
ListNode<T> * first;
};
#endif
2.为class Mylist链表类创建一个迭代器
我们常常这样使用STL中list的迭代器:
list<int> l;
for (list<int>::iterator i = l.begin(); i!= l.end();++i){cout<<*i<<endl;}
可以想见,iterator
不可能是一个指针,因为如果是一个指针,而链表的节点在内存中不是连续的,i++指向哪里呢
?其实,迭代器iterator是一个类,并且类中有一个ListNode节点指针
,毕竟迭代器迭代的是链表节点,必须要有数据成员来标明链表节点。这里需要把class Iterator声明为class ListNode的友元类,以便current能获取ListNode类的私有数据成员。迭代器类定义如下。
template <typename T>
class Iterator
{
public:
Iterator() :current(nullptr){}
Iterator(ListNode<T> *& node) :current(node){}
private:
ListNode<T> * current;//节点指针
};
2.1重载运算符
在第2节开头,我们对迭代器做了这样两种操作:++i、 *i、i!= l.end()和i = l.begin()。这都是重载运算符的结果。重载运算符也很简单,使用operator关键字+被重载的运算符即可
。如下:
//重载解引用符*
T& operator*()
{
return this->current->data;
}
//重载前缀自加运算符++
Iterator<T>& operator++()
{
this->current = this->current->rightlink;
return *this;
}
//重载不等比较运算!=
bool operator!=(const Iterator<T>& it)
{
return this->current != it.current;
}
我们也会对*i
重新赋值,如*i = 10
,那我们需要对赋值运算符再重载吗?答案是不需要,因为我们重载*的时候,返回的就是值得引用T&,当然是可以赋值的。
除此之外,还有
->:它返回迭代器当前指向节点数据的地址;
==:比较相等,相等返回真;
后缀++和前后缀–。
如下:
//重载后缀自加运算符++
Iterator<T> operator++(int)
{
Iterator<T> tmp = *this;
(*this).current = (*this).current->rightlink;
return tmp;
}
//重载前缀自减运算符--
Iterator<T>& operator--()
{
this->current = this->current->leftlink;
return *this;
}
//重载后缀自加运算符--
Iterator<T> operator--(int)
{
Iterator<T> tmp = *this;
(*this).current = (*this).current->leftlink;
return tmp;
}
//重载等于比较运算==
bool operator==(const Iterator<T>& it)
{
return this->current == it.current;
}
//重载->
T* operator->()
{
return &(this->current->data);
}
好了,到了这里,我们基本把list的迭代器写出来了。
3.完整的迭代器类实现代码
template <typename T>
class Iterator
{
public:
Iterator() :current(nullptr){}
Iterator(ListNode<T> *& node) :current(node){}
//重载解引用符*
T& operator*()
{
return this->current->data;
}
//重载前缀自加运算符++
Iterator<T>& operator++()
{
this->current = this->current->rightlink;
return *this;
}
//重载后缀自加运算符++
Iterator<T> operator++(int)
{
Iterator<T> tmp = *this;
(*this).current = (*this).current->rightlink;
return tmp;
}
//重载前缀自减运算符--
Iterator<T>& operator--()
{
this->current = this->current->leftlink;
return *this;
}
//重载后缀自加运算符--
Iterator<T> operator--(int)
{
Iterator<T> tmp = *this;
(*this).current = (*this).current->leftlink;
return tmp;
}
//重载不等比较运算!=
bool operator!=(const Iterator<T>& it)
{
return this->current != it.current;
}
//重载等于比较运算==
bool operator==(const Iterator<T>& it)
{
return this->current == it.current;
}
//重载->
T* operator->()
{
return &(this->current->data);
}
private:
ListNode<T> * current;//节点指针
};
4.测试迭代器的正确性
为了测试,我们再在下面的测试代码中为Mylist类先行添加了几个函数:
- Begin():由于有一个表头结点,它返回的是真正的第一个节点的迭代器
- End():它返回最后一个节点的迭代器,由于有表头节点,用表头节点作最后一个节点
- Push_front():在链表头插入数据
这3个函数的具体实现已在完整的测试代码中,均有详细注释。
Mylist.h
#ifndef _MY_LIST_H
#define _MY_LIST_H
template <typename T>
class Mylist;
template <typename T>
class Iterator;
/*******************
*定义双向链表的结点类
********************/
template <typename T>
class ListNode
{
friend class Mylist<T>;//Mylist类为ListNode类的友元,从而可以访问其私有成员
friend class Iterator<T>;//迭代器类Iterator为ListNode类的友元,从而可以访问其私有成员
private:
T data;
ListNode<T> * leftlink;
ListNode<T> * rightlink;
ListNode() :leftlink(nullptr), rightlink(nullptr){};
ListNode(T _data) :data(_data), leftlink(nullptr), rightlink(nullptr){}
};
/*******************
*定义双向链表类
********************/
template <typename T>
class Mylist
{
public:
Mylist();
~Mylist();
Iterator<T> Begin();//返回第一个结点的迭代器
Iterator<T> End();//返回表头结点的迭代器
void Push_front(const T& _data);//在表头插入
private:
ListNode<T> * first;
};
/*******************
*定义迭代器iterator
********************/
template <typename T>
class Iterator
{
friend class Mylist<T>;//Mylist类为Iterator类的友元,从而可以访问其私有成员
public:
Iterator() :current(nullptr){}
Iterator(ListNode<T> *& node) :current(node){}
//重载解引用符*
T& operator*()
{
return this->current->data;
}
//重载前缀自加运算符++
Iterator<T>& operator++()
{
this->current = this->current->rightlink;
return *this;
}
//重载后缀自加运算符++
Iterator<T> operator++(int)
{
Iterator<T> tmp = *this;
(*this).current = (*this).current->rightlink;
return tmp;
}
//重载前缀自减运算符--
Iterator<T>& operator--()
{
this->current = this->current->leftlink;
return *this;
}
//重载后缀自加运算符--
Iterator<T> operator--(int)
{
Iterator<T> tmp = *this;
(*this).current = (*this).current->leftlink;
return tmp;
}
//重载不等比较运算!=
bool operator!=(const Iterator<T>& it)
{
return this->current != it.current;
}
//重载等于比较运算==
bool operator==(const Iterator<T>& it)
{
return this->current == it.current;
}
//重载->
T* operator->()
{
return &(this->current->data);
}
ListNode<T>* getIter()
{
return current;
}
private:
ListNode<T> * current;//节点指针
};
//构造和析构函数
template <typename T>
Mylist<T>::Mylist()
{
first = new ListNode<T>;//生成一个表头结点,它里面不带数据
first->leftlink = first->rightlink = first;//左右都指向自己
}
template <typename T>
Mylist<T>::~Mylist()
{
}
template <typename T>
Iterator<T> Mylist<T>::Begin()
{
return Iterator<T>(first->rightlink);
}
template <typename T>
Iterator<T> Mylist<T>::End()
{
return Iterator<T>(first);
}
template <typename T>
void Mylist<T>::Push_front(const T& _data)
{
ListNode<T> *newNode = new ListNode<T>(_data);
//1.确定插入节点的指向
newNode->leftlink = first;
newNode->rightlink = first->rightlink;
//2.确定表头结点first的指向
first->rightlink = newNode;
//3.改变插入节点的后一个节点的左指向
newNode->rightlink->leftlink = newNode;
}
#endif
main.cpp
#include <iostream>
#include <list>
#include "Mylist.h"
using namespace std;
int main()
{
cout << "***********STL::list**********" << endl;
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
for (list<int>::iterator i = l1.begin(); i!= l1.end(); i++){
cout << i.operator->() << endl;
cout << *i << endl;
}
list<int>::iterator it3 = ++l1.begin();
cout << "++ " << *it3 << endl;
it3 = --l1.end();
cout << "-- " << *it3 << endl;
cout << "***********自己实现**********" << endl;
Mylist<int> l;
l.Push_front(3);
l.Push_front(2);
l.Push_front(1);
for (Iterator<int> it1 = l.Begin(); it1 != l.End(); it1++){
cout << it1.operator->() << endl;
cout << *it1 << endl;
}
Iterator<int> it2 = ++l.Begin();
cout << "++ " << *it2 << endl;
it2 = --l.End();
cout << "-- " << *it2 << endl;
system("pause");
return 0;
}
好了,list的简易迭代器就完成了,下一篇文章将实现list的常用操作之插入操作。
如果有疑问,欢迎评论区下方留言;本人水平有限 ,如有错误,也欢迎在评论区下方批评指正。若是喜欢本文,就帮忙点赞吧!
系列文章说明:
C++ STL::list常用操作及底层实现(上)——实现list迭代器
C++ STL::list常用操作及底层实现(中1)——实现list常用操作之插入(insert、push_front、push_back、splice)
C++ STL::list常用操作及底层实现(中2)——实现list常用操作之删除(erase、remove、pop_front、pop_back、clear)
C++ STL::list常用操作及底层实现(下)——实现list其他常用操作(reverse、assign、front、back)