前言`
list是数据结构中的双向链表,无需多言,懂的都懂!stl中的list封装了更多的功能,list相比vector,他的迭代器设计更符合一般认知。看看吧。
一、list的基础设计
list的元素在内存空间中是离散的,意味着我们不能简单地用指针来作为迭代器,因为指针进行++,–一类的操作时是在内存空间中顺序访问的,这满足不了list的需求,我们需要对迭代器进行专门设计。同样的,list里面的每个元素他所暗含的不仅仅是值,还有元素之间的关系,具体的一个例子就是list的每一个节点除了有自己的值只对外还应该包含他的上一个元素和下一个元素。
总之,看代码把
template <class T>
struct __list_node {
typedef void* void_pointer;
void_pointer prev;
void_pointer next;
T data;
|
template <class T, class Ref, class Ptr>
struct __list_iterator {
//为什么要这么设计呢?
//为iterator赋予iterator的作用是什么呢,懂了,一个字,图方便
//但是为什么要把Ref和Ptr都加进去呢?
//为了灵活设置?gpt给出的回答是这样的
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, Ref, Ptr> self;
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
link_type node;
__list_iterator(link_type x) : node(x) {}
__list_iterator():node(nullptr){}
//用一个另外的迭代器来初始化这一个迭代器
__list_iterator(const iterator& x) : node(x.node){}
bool operator ==(const self& x)const {
return node == x.node;
}
bool operator != (const self& x)const {
return node != x.node;
}
//返回的是reference,所以对返回值操作能改变(*node)的data值
reference operator*()const {return (*node).data}
//返回的竟是指针所指内容的地址,为什么不返回node的地址呢?
//我认为这里的设计不好
pointer operator->() const {return &(operator*());}
self& operator++(){
//注意我们在设计listnode的时候使用的是void*,因此这里需要进行一下改变
node = (link_type)node->next;
return *this;
}
self operator++(int){
self temp = *this;
++*this;
return temp;
}
self& operator--(){
node = (link_type)node->prev;
return *this;
}
self operator--(int){
self tmp = *this;
--*this;
return tmp;
}
}
看了list的节点和迭代器的设计,接下来就是list这个整体的设计了
template <class T, class Alloc = alloc>
class list{
protected:
typedef __list_node<T> list_node;
public:
typedef list_node* link_type;
protected:
link_type node;
iterator begin(){return (link_type)(node->next)}
iterator end(){return node;}
bool empty(){
return node == node->next;
}
size_type size() const{
size_type result;
distance(begin(), end(), result);
return result;
}
reference front(){
return *begin();
}
reference back(){
return *(--end());
}
}
上述代码可以看出list作为一个容器,他对每一个节点进行了封装,并提供了相关的接口,例如得到list的头结点,尾结点之类的。隐藏在这背后的是list的构造与内存管理。
template <class T, class Alloc = alloc>
class list{
typedef __list_node<T> list_node;
typedef simple_alloc<list_node, Alloc> list_node_allocator;
protected:
link_type get_node(){
return list_node_allocator::allocate();
}
void put_node(link_type posision){
list_node_allocator::deallocate(position);
}
link_type create_node(const T& x){
link_type pos = get_node();
construct(&pos->data, x);//这里需要注意,通过给出一个值来构造的是node中的data,显然这里如果需要是要根据各种情况写出不同的重载方法
return pos;
}
void destroy_node(link_type pos){
destroy(&pos->data);//这里同样要注意destroy的依然是data那部分
put_node(pos);
return;
}
public:
list() {
empty_initialize();
}
void push_back(const T& x){insert(end(), x);}
iterator insert(iterator position, const T& value){
link_type nodetmp = create_node(x);
nodetmp->next = position;
nodetmp->prev = position->prev;
position->prev = nodetmp;
nodetmp->prev->next = nodetmp;
return nodetmp;
//注意一下这里的转换方式
//iterator的定义是__list_iterator<T, T&, T*> ,而他恰好有link_type的构造函数,通过隐式构造,完成转换
}
private:
void empty_initialize(){
node = create_node();
node->data = 0;
node->next = node;
node->prev = node;
return ;
}
}
如果看过了vector,那么这里的应该是没有什么新意的。destroy,construct,allocate,deallocate。不过值得注意的是,为什么侯捷把insert归类到内存管理这一小节。我们应该要清楚地看到insert函数里面是使用了内存管理相关的甘薯create_node,可能正因为如此作者这么归类。
二、list的元素操作
正如vector中有各种erase之类的对元素的操作,list中也有这些操作,这些操作的难点在于其算法。
看看吧
//头插,我们应该在begin之前插入
void push_front(const value_type& t){insert(begin(), t);}
void push_back(const value_type& t){insert(end(), t);}
//将position处的元素删除掉
iterator erase(iterator position){
iterator tmp = position.node->next;
position.node->prev->next = position.node->next;
position.node->next->prev = position.node->prev;
destroy_node(position.node);
return tmp;
}
void pop_front(){
erase(begin());
}
void pop_back(){
iterator tmp = end();
--tmp;
erase(tmp);
}
template <class T, class Alloc>
void list<T, Alloc>::clear(){
link_type cur = node->next;
while(cur != node){
link_type tmp = (link_type)cur->next;
destroy(cur);
cur = tmp;
}
node->next = node;
node->prev = node;
}
template<class T, class Alloc>
void list<T, Alloc>::remove(const T& value){
iterator first = begin();
iterator last = end();
while(first != first){
if(*first == value)
iterator tmp = ++first;
erase(first);
first = tmp;
}
}
void list<T, Alloc>::unique(){
iterator first = begin();
iterator last = end();
T tmp = *first;
++first;
while(first != last){
if(tmp == *first){
iterator next = ++first;
erase(fisrt);
first = next;
}else{
tmp = *fisrt;
++first;
}
}
}
//只要思路正确,一定能写对!
void transfer(iterator position, iterator first, iterator last){
if(position != last){
first.node->prev->next = last.node;
(link_type) node = last.node->prev;
last.node->prev = first.node->prev;
first.node->prev = position.node->prev;
last.node->prev->next = position.node;
position.node->prev->next = first;
position.node->prev = node;
}
}