额额额,浅学了list,老规矩,模拟实现一下list,能让我们更好的理解list。若有兴趣,不妨垂阅!
目录
同样的,我不可能把list所有接口都模拟实现出来,我也没有这个能力。So,模拟实现一些简单常用的接口。
本工程包括2个文件:
mylist.h:用于模拟实现list。
test.cpp:用于测试模拟实现的list接口是否达到预期。
1.模拟实现list
以下代码均放到mylist.h文件中。
1.1.list节点
由于list底层是带头(哨兵位)双向循环链表。所以模拟实现的list节点需要能找到前驱节点和后继节点,这点是毋庸置疑的。那么,我们将list节点设计如下:
namespace HD
{
//list节点
template<typename T>
struct listNode
{
typedef listNode node;//类里面可以将listNode<T>用listNode替代
T _val;
node* _prev;
node* _next;
};
}
_val用于存储数据,_prev用于指向前驱节点,_next用于指向后继指针。
1.2.list正向迭代器
好的,由于list反向迭代器较为复杂,所以我只模拟实现正向迭代器。正向迭代器分为:普通正向迭代器、const正向迭代器。一个一个来模拟实现:
1.普通正向迭代器
我们知道迭代器是模拟指针的行为,但是却不是所有的迭代器都能直接用原生指针实现。例如:
string的正向迭代器,由于其底层是字符顺序表,可以直接用原生指针来实现迭代器;
但是如list底层是链表,正向迭代器就不能直接用原生指针实现,好比如,list<int>::iterator it; ++it;,如果该list的正向迭代器是原生指针实现的,++操作能让it指向下一个节点吗?显然不能,因为链表的物理空间不是连续的。
那么我们怎么办?
别忘了操作符重载这里利器,我们可以用类封装一下原生指针来实现普通正向迭代器:
namespace HD
{
//list 普通正向迭代器
template <typename T>
struct listIterator
{
typedef listNode<T>* pNode;
typedef listIterator<T> self;
pNode _pNode;
listIterator<T>(pNode pNode = nullptr)
:_pNode(pNode)
{
}
T& operator*()
{
return (*_pNode)._val;
}
self operator++(int)//后置++
{
self it(_pNode);
_pNode = _pNode->_next;
return it;
}
self& operator++()//前置++
{
_pNode = _pNode->_next;
return *this;
}
self operator--(int)//后置--
{
self it(_pNode);
_pNode = _pNode->_prev;
return it;
}
self& operator--()//前置--
{
_pNode = _pNode->_prev;
return *this;
}
bool operator!=(const self& it)
{
return _pNode != it._pNode;
}
bool operator==(const self& it)
{
return _pNode == it._pNode;
}
T* operator->()
{
return &_pNode->_val;
}
};
}
其实仔细看,这个类只有一个成员变量,就是list节点的指针_pNode。把这个指针用类封装一下,提供各种运算符重载作为该类的成员函数,就成了。本质上,这个类不还是list节点指针吗?但是这个list节点指针的行为却不是原生指针的行为能达到的。。。。。好好体会!!!!
2.const正向迭代器
普通正向迭代器和const正向迭代器基本一样。我们想想,const正向迭代器区别于普通正向迭代器,无非就是const正向迭代器指向的节点存储的数据不能更改吗?
SO:
namespace HD
{
//list const正向迭代器
template <typename T>
struct listConstIterator
{
typedef listNode<T>* pNode;
typedef listConstIterator<T> self;
pNode _pNode;
listConstIterator<T>(pNode pNode = nullptr)
:_pNode(pNode)
{
}
const T& operator*()
{
return (*_pNode)._val;
}
self operator++(int)//后置++
{
self it(_pNode);
_pNode = _pNode->_next;
return it;
}
self& operator++()//前置++
{
_pNode = _pNode->_next;
return *this;
}
self operator--(int)//后置--
{
self it(_pNode);
_pNode = _pNode->_prev;
return it;
}
self& operator--()//前置--
{
_pNode = _pNode->_prev;
return *this;
}
bool operator!=(const self& it)
{
return _pNode != it._pNode;
}
bool operator==(const self& it)
{
return _pNode == it._pNode;
}
const T* operator->()
{
return &_pNode->_val;
}
};
}
仔细看,实现起来跟普通正向迭代器的区别就在于operator*和operator->这2个函数的返回值不同罢了。至于operator->这个函数到底干啥的,一会儿说的。
3.普通正向迭代器和const正向迭代器合二为一
看到普通正向迭代器和const正向迭代器这两个类模板基本一样,那么我们完全可以写一个类模板来代替以上2个类模板:
namespace HD
{
//list正向迭代器
template <typename T,typename Ref,typename Ptr>
struct listIterator
{
typedef listNode<T>* pNode;
typedef listIterator<T,Ref,Ptr> self;
pNode _pNode;
listIterator<T,Ref,Ptr>(pNode pNode=nullptr)
:_pNode(pNode)
{
}
Ref operator*()
{
return (*_pNode)._val;
}
self operator++(int)//后置++
{
self it(_pNode);
_pNode = _pNode->_next;
return it;
}
self& operator++()//前置++
{
_pNode = _pNode->_next;
return *this;
}
self operator--(int)//后置--
{
self it(_pNode);
_pNode = _pNode->_prev;
return it;
}
self& operator--()//前置--
{
_pNode = _pNode->_prev;
return *this;
}
bool operator!=(const self& it)
{
return _pNode != it._pNode;
}
bool operator==(const self& it)
{
return _pNode == it._pNode;
}
Ptr operator->()
{
return &_pNode->_val;
}
};
}
通俗易懂讲,当Ref、Ptr是T&、T*的时候,不就是普通正向迭代器吗?
当Ref、Ptr是const T&、const T*的时候,不就是const正向迭代器吗?
1.3.list模拟实现
namespace HD
{
//list
template<typename T>
class list
{
private:
typedef listNode<T> node;
typedef listNode<T>* pNode;
typedef T value_type;
typedef size_t size_type;
private:
pNode _pHead;
size_t _size;
public:
typedef listIterator<T,T&,T*> i