目录
引言
一:STL源代码中关于list的成员变量的介绍
二:模拟实现list
1.基本结构
2.普通迭代器 +const迭代器的结合
3.构造+拷贝构造+析构+赋值重载 +清空
4.insert+erase+头尾插入删除
5.打印不同数据类型的数据《使用模板加容器来完成》
三:全部代码加测试用例链接
接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧
引言
在前面的数据结构中已经实现了c版本的list-双向循环链表,但在c++中注重的是分装,对于操作进行封装处理,对于我们使用便是方便了不少,但模拟实现的话还是有许多要注意的细节点,尤其是list的迭代器较复杂,需要认真理解
一:STL源代码中关于list的成员变量的介绍
在源代码中使用一个头结点的指针来 模拟实现的,但在文档中只介绍list是一个双向链表的容器,并没有说明是否带哨兵位了,之时就得观察初始化函数和构造函数等来观察
所以从这里就可以看出list的结构就是一个带头循环双向链表,接下来我们就开始模拟实现
二:模拟实现list
1.基本结构
template<class T>//泛型编程
struct list_node
{T _data;list_node<T>* _prev;list_node<T>* _next;list_node(const T &x=T())//缺省值为匿名对象:_data(x),_prev(nullptr),_next(nullptr){}
};template<class T>
class list
{typedef list_node<T> node;
public:private:node* head;size_t size;//方便size()接口,省去便利的开销
};
2.普通迭代器 +const迭代器的结合
对于用迭代器来遍历list的数据,就不能像vector和string那样,直接使用,因为
list<int>::iterator it=lt1.begin();
while(it!=lt1.end())
{
cout<<*it<<" ";
++it;
}
cout<<endl;
对于这里面的*it和++it和it!=lt1.end()这些操作是不能直接支持的,因为*it是要取data的数据,++it就是要使_node=_node->next等,这些是和vector,string是不一样的,所以得自己手动支持,运用运算符重载+函数封装,我们先来观察源代码中的实现
我们先来实现普通迭代器
template<class T>
struct _list_iterator
{typedef list_node<T> Node;typedef _list_iterator<T> self;Node* node;_list_iterator(Node*node):_node(node){}self& operator++(){_node = _node->next;return *this;}self& operator--(){_node = _node->prev;return *this;}self operator++(int){self tmp(*this);_node = _node->next;return tmp;}self operator--(int){self tmp(*this);_node = _node->prev;return tmp;}T& operator*(){return _node->data;}T* operator->(){return &_node->data;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};
template<class T>
class list
{typedef list_node<T> node;
public:typedef _list_iterator<T> iterator;iterator begin(){//return iterator(_head->_next);return _head->_next;}iterator end(){return _head;}
对于这个结构可以这样来理解
我们知道const对象使用的是const迭代器那么在iterator之前加const的话,这样const iterator指的是迭代器本身不能修改,但在遍历过程迭代器本身是要改变的,如++it等,所以不能这样写
应该为const_iterator这是一个重新定义的一个类型,要做到迭代器本身可以改变,但迭代器指向的内容不能改变
在原来普通迭代器的基础上重载出一份const迭代器的版本如
但这样的话对于普通迭代器和const迭代器两份代码是由许多相同的代码的,比较冗余,所以在源代码中提出了一份更好的解决方案来解决这个问题
在原来基础上多添加两个参数,这样对于同一个类模板,实例化参数不同就是不同的类型
对于上面的代码就要优化为更完善的代码,展示主要优化的代码
template<class T,class Ref,class Ptr>
struct _list_iterator
{typedef list_node<T> Node;typedef _list_iterator<T,Ref,Ptr> self;Node* node;Ref operator*(){return _node->data;}Ptr operator->(){return &_node->data;}typedef _list_iterator<T,T&,T*> iterator;typedef _list_iterator<T, const T&, const T*> const_iterator;//typedef __list_const_iterator<T> const_iterator;iterator begin(){//return iterator(_head->_next);return _head->_next;}iterator end(){return _head;}const_iterator begin()const{//return iterator(_head->_next);return const_iterator(_head->_next);}const_iterator end()const{return const_iterator(_head);}
对于->编译器是做处理的,将两个->优化为一个详细看代码及测试 用例3中
3.构造+拷贝构造+析构+赋值重载 +清空
构造时要使用带头循环双向循环链表的初始化操作,观看https://blog.csdn.net/Miwll/article/details/136593441?spm=1001.2014.3001.5502
void empty_init()
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;
}
list()
{empty_init();
}
list(const list<T>& t)//拷贝构造实现为深拷贝
{empty_init();for (auto e : t){push_back(e);}
}
/*list<int>& operator=(const list<int>& lt)
{if (this != <){clear();for (auto e : lt){push_back(e);}}return *this;
}*/
void swap(list<T>& t)
{std::swap(_head,t._head);std::swap(_size, t._size);
}
list<int>& operator=(const list<int>& lt)
{swap(lt);return *this;
}
~list()
{clear();delete _head;_head =nullptr;
}
void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}
4.insert+erase+头尾插入删除
void push_back(const T& x)
{insert(end(), x);
}void push_front(const T& x)
{insert(begin(), x);
}void pop_front()
{erase(begin());
}void pop_back()
{erase(--end());
}
iterator insert(iterator pos, const T& x)
{Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;++_size;return iterator(newnode);
}
iterator erase(iterator pos)
{Node* cur = pos._node;Node* next = cur->_next;Node* prev = cur->_prev;delete cur;prev->_next = next;next->_prev = prev;--_size;return iterator(next);
}
size_t size()
{return _size;
}
5.打印不同数据类型的数据《使用模板加容器来完成》
使用模板来打印自定义类型数据,注意这里加的typename
list<T>未实例化的类模板,编译器不能去他里面去找 编译器就无法list<T>::const_iterator是内嵌类型,还是静态成员变量 前面加一个typename就是告诉编译器,这里是一个类型,等list<T>实例化 再去类里面去取
三:全部代码加测试用例链接
https://gitee.com/lin-ciyu/cplusplus/tree/master/my_list/my_list