目录
引言
一: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