C++ STL list模拟
在C++中,我们经常使用STL,那个在那些我们常用的数据结构vector,list的背后,又是如何实现的呢?特别是当我们使用iterator对容器进行遍历的时候,我们也能够想整数一样进行 ++ 运算。下面通过一个例子来建立一个slist,使得它能够通过iterator进行访问。
我们要实现的功能是:建立一个list存储任意个数,然后输入一个数,通过find函数查找是否在list中存在。所以我们首先要编写一个find函数,find函数的要求是能够操作多种容器,能够查找多种数据类型(int,double),所以应该建立一个模版函数。
(实际在STL中,这类函数应该叫算法,它通过iterator对容器进行遍历和操作)
所以,这个find函数应该是这样的:
template<class IteratorType,class T> IteratorType find(IteratorType begin,IteratorType end,const T & Value) { while (begin != end && *begin != Value) { ++begin; } return begin; }
在find函数中,我们使用模版,查找的关键字的不同类型由T处理,T可以取double,int等。另外,find的前两个参数传入的就是iterator类型的参数,它的作用就像
一个指针,分别指向容器中两个元素。查找的过程就是在这两个元素之间进行查找。从该函数还看出,begin并不是整数类型的,但是也能够通过++操作符实现遍历。
所以iterator需要重载++操作符。
假设我们要建的list叫slist,可定义如下类:
#ifndef SIMPLELIST_H #define SIMPLELIST_H #include <cassert> namespace br_stl{ /** @brief The slist class.<br/> * Description:Descripe the template list container */ template<class T> class slist { public: typedef T value_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T& reference; /** @brief The constructor of slist function Copy constructor ,destructor and assignment operator are omitted. @return no return */ slist():firstElement(0),Count(0){} /** \brief The push_front function. Creates a new list element and inserts it at the beginning of the list. \param Datum a parameter of type const T& \return void */ void push_front(const T& Datum); private: struct ListElement { T Data; ListElement* Next; //constructor of the struct ListElement(const T& Datum,ListElement* p):Data(Datum),Next(p){} }; ListElement* firstElement; size_t Count; public: class iterator { public: typedef std::forward_iterator_tag iterator_category; typedef T value_type; typedef T* pointer; typedef T& reference; typedef size_t size_type; typedef ptrdiff_t difference_type; iterator(ListElement* Init=0):current(Init){} //dereference T& operator*() { return current->Data; } //dereference const T& operator*() const { return current->Data; } //prefix ++ iterator& operator++() { if (current){ current = current->Next; } return *this; } //postfix iterator operator++(int) { iterator temp = *this; ++*this; return temp; } bool operator==(const iterator& x) const { return current == x.current; } bool operator!=(const iterator& x) const { return current != x.current; } private: ListElement *current;//pointer to the current element. };//iterator /** \brief The begin function.<br/> Usage:<br/> container.begin() \return iterator that point to the first of the container. */ iterator begin() const{ return iterator(firstElement); } /** \brief The end function. \return iterator that point to the next one of the last element of the container(NULL). */ iterator end() const{ return iterator(); } };//slist template<class T> void slist<T>::push_front(const T& Datum) { firstElement = new ListElement(Datum,firstElement); Count++; } }//namespace br_stl template<class Iterator> int operator-(Iterator second,Iterator first) { int count = 0; while (first != second && first != Iterator()) { ++first; ++count; } assert(first == second); return count; } #endif
这个类我们重点关注如下几点:
1、在内部定义了一个私有的结构体:
struct ListElement { T Data; ListElement* Next; //constructor of the struct ListElement(const T& Datum,ListElement* p):Data(Datum),Next(p){} };
结构体其实就是我们常见的单链表节点,然后结构体还定义了一个方法(C++允许结构体有方法),相当于构造函数。这个构造函数很有意思,即我们在新建一个节点的时候,节点就通过构造函数自动插入到链表了,无需在经过繁琐的指针操作。
例如 firstElement = new ListElement(23,firstElement) 将firstElement赋值给了一个新节点的Next,相当于链表的头插法。
2、除了结构体外,还定义了一个内部class,叫iterator,slist正是通过这个叫iterator的类对其本身进行遍历。
iterator类要实现的就是几个操作符的重载,通过++符号实现p = p-> next的链表遍历操作。
现在写一段测试代码,
#include <iostream> #include "SimpleList.h" using namespace std; //we do not need to modify the value here typedef const double* IteratorType1; //IteratorType find(IteratorType begin,IteratorType end,const int & Value); template<class IteratorType,class T> IteratorType find(IteratorType begin,IteratorType end,const T & Value) { while (begin != end && *begin != Value) { ++begin; } return begin; } int main() { const int count = 100; // double aContainer[count]; br_stl::slist<int> aContainer; // IteratorType1 begin = aContainer; //IteratorType1 end = aContainer+count; for (int i=count-1;i>=0;i--) { aContainer.push_front(i*2); } int Number = 0; while (Number != -1) { cout<<"Please Input a Number(-1 = end):"; cin>>Number; if (Number != -1) { br_stl::slist<int>::iterator pos = find(aContainer.begin(),aContainer.end(),Number); if (pos != aContainer.end()) { cout<<"Find at "<<pos-aContainer.begin()<<endl; } else { cout<<"not found!"<<endl; } } } return 0; }
运行结果就是输入一个数,程序给出是否能在列表中找到。