目录
- list的实现结构
- 节点的实现
- 迭代器的实现
- 第一个模板参数T
- 第二个模板参数Ref
- 第三个模板参数Ptr
- 实现list中的接口函数
- 插入和删除
- 赋值重载和拷贝构造
- 析构函数
- 总结
list的实现结构
STL库中的list的结构是双向循环链表,所以我们这里也实现一个双向循环链表
我们这里采取带头双向循环链表
为了方便节点间的操作,可以将节点封装成一个结构体
list中的迭代器是双向迭代器,只支持++
和--
操作,不支持+
,-
,所以不能用一个指针用作迭代器,所以还需要将迭代器封装成一个结构体,在结构体中对迭代器的一些操作进行实现
节点的实现
template<class T>struct __list_node{__list_node<T>* _next;__list_node<T>* _prev;T _val;__list_node(const T& val = T()):_next(nullptr),_prev(nullptr),_val(val){}};
主义,这是一个类模板,类名是__list_node
,类型是__list_node<T>
所以2个指针_next
,_prev
的类型是__list_node<T>
,分别指向前一个节点和后一个节点
现在,我们就可以把头节点作在为ist类中的成员变量
节点的类型为__list_node<T>
,比较麻烦,这里我们可以 typedef __list_node<T> Node
因为在类外我们不会直接用到Node
,所以把typedef __list_node<T> Node
放到private中就可以
同时实现构造函数,让_head
的_next
指向自己,_prev
也指向自己
template<class T >class list{typedef __list_node<T> Node;public:list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}private:Node* _head;};
迭代器的实现
list的迭代器是双向迭代器,只支持++
和--
操作,不支持+
,-
所以不能只是简单的服用指针
要对指针进行封装,在结构体中实现++
,--
,*
,->
等操作
第一个模板参数T
list迭代器的底层其实还是Node*
类型的指针
template<class T>struct __list__iterator{typedef __list_node<T> Node;typedef __list__iterator<T> Iter;__list__iterator(Node* pnode):_pnode(pnode){}Node* _pnode;};
因为总使用
__list_node<T>
和__list__iterator<T>
比较麻烦,所以typedef __list_node<T> Node; typedef __list__iterator<T> Iter;
++
操作实际上的底层是当前迭代器指向当前指向节点的下一个节点
--
操作实际上的底层是当前迭代器指向当前指向节点的前一个节点
所以下面就可以实习++
,--
操作
Iter& operator++()
{_pnode = _pnode->_next;return *this;
}Iter operator++(int)
{Iter tmp(*this);_pnode = _pnode->_next;return tmp;
}Iter& operator--()
{_pnode = _pnode->_prev;return *this;
}Iter operator--(int)
{Iter tmp(*this);_pnode = _pnode->_prev;return tmp;
}
解引用操作也就是返回迭代器指向节点中的值
T& operator*()
{return _pnode->_val;
}T* operator->()
{return &_pnode->_val;
}
!=
和==
就是比较迭代器下面的指针
bool operator!=(const Iter it)
{return _pnode != it._pnode;
}bool operator==(const Iter it)
{return _pnode == it._pnode;
}
接下来在list
中实现begin()
和end()
函数
__list__iterator<T>
麻烦,typedef成iterator
typedef __list__iterator<T> iterator;
我们需要在类外使用到iterator
,所以这句typedef
要放到public中
list的结构如下:
因为begin()
返回的是指向第一个有效节点的迭代器,所以_head->_next
就是第一个有效节点
iterator begin()
{return iterator(_head->_next);
}
end()
返回的是最后一个节点的下一个位置,这里最后一个节点的下一个位置就是_head
iterator end()
{return iterator(_head);
}
第二个模板参数Ref
现在有一个问题,就是如何实现const_iterator
,在前面iterator
的基础上,我们可以在封装一个const_iterator
的结构体,其中的返回值改为const T&
或const T*
但是再封装一个,就会导致代码冗余
这里我们引入第二个模板参数class Ref
这个参数是用来接收返回引用的类型的可以是T&
也可以是const T&
所以这时的*
重载返回值就是Ref
了
Ref operator*()
{return _pnode->_val;
}
第三个模板参数Ptr
同理,对于->
重载的返回类型是指针
引入第三个模板参数Ptr
它是用来接收T*
和const T*
的
Ptr operator->()
{return &_pnode->_val;
}
现在就可以在list中实现cbegin
和cend1
了
首先还是将typedef
出iterator
和const_iterator
typedef __list__iterator<T, T&, T*> iterator;
typedef __list__iterator<T, const T&, const T*> const_iterator;
const_iterator begin()const
{return const_iterator(_head->_next);
}const_iterator end()const
{return const_iterator(_head);
}
实现list中的接口函数
插入和删除
这里的insert
和erase
的实现和C语言中实现链表的操作一样,比较容易
这里唯一需要注意的是:insert
函数返回插入新节点的迭代器,erase
函数返回删除节点后,删除节点下一个节点位置的迭代器
iterator insert(iterator it, const T& val)
{Node* newnode = new Node(val);Node* cur = it._pnode;Node* prev = it._pnode->_prev;newnode->_next = cur;cur->_prev = newnode;prev->_next = newnode;newnode->_prev = prev;_size++;return newnode;
}iterator erase(iterator it)
{assert(it != _head);//需要检查一下it是否是头节点位置的迭代器,头节点不能删除Node* del = it._pnode;Node* prev = it._pnode->_prev;Node* nex = it._pnode->_next;prev->_next = nex;nex->_prev = prev;_size--;delete del;return nex;
}
实现了insert
和erase
后,通过复用这两个函数,就可以实现push_back
,push_front
,pop_back
,pop_front
函数了
void push_back(const T& val)
{insert(end(), val);
}void push_front(const T& val)
{insert(begin(), val);
}void pop_back()
{erase(_head->_prev);
}void pop_front()
{erase(_head->_next);
}
赋值重载和拷贝构造
赋值重载的实现,可以使用现代写法,和在vector
中赋值重载的写法类似,使用swap
void swap(list<T> lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}//赋值重载现代写法
list<T> operator=(list<T> lt)
{swap(lt);return *this;
}
拷贝构造,我们先构造出一个list,然后用push_back
向里面插入节点
//拷贝构造
list(const list<T>& lt)
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;for (auto& e : lt){push_back(e);}}
这里有一点:
库中复制拷贝和运算符重载中参数列表中形参类型不是list<T>
,而是list类型没有指定后面的模板参数
析构函数
析构函数需要清理空间,对于链表来说,我们不能只清理头节点
我们需要清理掉有有效节点后,最后清理掉头节点
这里我们定义一个清理掉有有效节点的函数
使用erase
+循环清理节点
void clear()
{iterator it = begin();while (it != end()){it = erase(it);}_size = 0;
}
然后在销毁函数中调用clear
函数,再清理掉头节点
~list()
{clear();delete _head;_head = nullptr;
}
总结
此时,list的模拟实现就基本完成了
- list的模拟实现中,有难度的地方就是把指针封装成迭代器,在封装中实现以及限制了迭代器的作用
- 对于复杂类型,我们可以
typedef
出一个简单的别名,对于这个别名,如果要在类外使用就定义在public里,如果不允许在类外使用,就定义在private中
完整代码:
namespace my_list
{template<class T>struct __list_node{__list_node<T>* _next;__list_node<T>* _prev;T _val;__list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}};//typedef __list__iterator<T,T&,T*> iterator;//typedef __list__iterator<T, const T&, const T*> const_iterator;template<class T, class Ref, class Ptr>struct __list__iterator{typedef __list_node<T> Node;typedef __list__iterator<T, Ref, Ptr> Iter;__list__iterator(Node* pnode):_pnode(pnode){}Node* _pnode;bool operator!=(const Iter it){return _pnode != it._pnode;}bool operator==(const Iter it){return _pnode == it._pnode;}Iter& operator++(){_pnode = _pnode->_next;return *this;}Iter operator++(int){Iter tmp(*this);_pnode = _pnode->_next;return tmp;}Iter& operator--(){_pnode = _pnode->_prev;return *this;}Iter operator--(int){Iter tmp(*this);_pnode = _pnode->_prev;return tmp;}Ref operator*(){return _pnode->_val;}Ptr operator->(){return &_pnode->_val;}};template<class T >class list{typedef __list_node<T> Node;public:typedef __list__iterator<T, T&, T*> iterator;typedef __list__iterator<T, const T&, const T*> const_iterator;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator begin()const{return const_iterator(_head->_next);}const_iterator end()const{return const_iterator(_head);}void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_init();}list(size_t n, const T& val = T()){empty_init();for (int i = 0; i < n; i++){push_back(val);}}~list(){clear();delete _head;_head = nullptr;}//拷贝构造list(const list<T>& lt)//list(const list& lt) //库中的拷贝构造是这么定义的,list类型没有指定后面的模板参数{empty_init();for (auto& e : lt){push_back(e);}}void swap(list<T> lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}//赋值重载现代写法list<T> operator=(list<T> lt)//list operator=(list lt) //库中的赋值重载是这么定义的,list类型没有指定后面的模板参数{swap(lt);return *this;}size_t size() const{return _size;}bool empty() const{if (_head->_next == _head || _head->_prev == _head)return true;elsereturn false;}iterator insert(iterator it, const T& val){Node* newnode = new Node(val);Node* cur = it._pnode;Node* prev = it._pnode->_prev;newnode->_next = cur;cur->_prev = newnode;prev->_next = newnode;newnode->_prev = prev;_size++;return newnode;}iterator erase(iterator it){assert(it != _head);//需要检查一下it是否是头节点位置的迭代器,头节点不能删除Node* del = it._pnode;Node* prev = it._pnode->_prev;Node* nex = it._pnode->_next;prev->_next = nex;nex->_prev = prev;_size--;delete del;return nex;}void push_back(const T& val){insert(end(), val);}void push_front(const T& val){insert(begin(), val);}void pop_back(){erase(_head->_prev);}void pop_front(){erase(_head->_next);}void print(){iterator it = begin();while (it != end()){std::cout << *it << " ";it++;}std::cout << std::endl;}void clear(){iterator it = begin();while (it != end()){it = erase(it);//经过erase后的迭代器会失效,为了让循环继续下去,用it接收erase返回的迭代器(删除位置迭代器的下一个位置的迭代器)}_size = 0;}private:Node* _head;size_t _size; //加一个_size成员变量,如果不设置_size,在实现size()函数时就需要遍历list,时间复杂度为O(n)};}