概述

  • SGI STL的list是一个双向链表,单向链表是slist,其不在标准规格之内
  • 单向和双向链表的区别在于,单向链表的迭代器是单向的 Forward Iterator,双向链表的迭代器属于双向的Bidirectional Iterator。因此很多功能都被受限
  • 但是单向链表的耗用的空间更小,某些操作更快
  • 注意事项:插入操作会将新的元素插入到指定的位置之前,而不是之后的位置。但是单向链表无法回头确定前一个位置,因此需要从头部开始重新找起,即 除了在单向链表的起始处附近其余的地方进行insert 或者 erase都是不合适的操作。这也是单向和双向链表之间最大的差异,因此 单向链表提供了 insert_after() 和 erase_after() 供灵活使用
  • 基于上述的考虑,slist 也不提供push_back() 只提供push_front(),因此一定要注意单向链表元素的顺序和 元素的插入进来的顺序 相反

slist的节点

  • 单向链表的迭代器比双向链表更为复杂一些,运用了继承关系,因此在型别转换层面上有着复杂的表现

#include <iostream>//单向链表的节点的基本结构
struct __slist_node_base{__slist_node_base* next;
};//单向链表的节点结构
template <class T>
struct __slist_node : public __slist_node_base{T data;
};//全局函数 已知某一个节点,插入新的节点于其后
inline __slist_node_base* __slist_make_link(__slist_node_base* prev_node,__slist_node_base* new_node){//令new_node节点的下一个节点为prev节点的下一节点new_node->next = prev_node->next->next;prev_node->next = new_node; //令prev节点的next指针指向new节点return new_node;
}//全局函数 单向链表的大小(元素的个数)
inline std::size_t __slist_size(__slist_node_base* node){std::size_t result = 0;for( ; node != 0;node = node->next){++result; //一个一个累计}return result;
}

slist的迭代器

  • 实际构造的时候 注意迭代器和节点之间的关系
//单向链表的迭代器的基本结构
struct __slist_iterator_base{typedef std::size_t size_type;typedef std::ptrdiff_t difference_type;typedef std::forward_iterator_tag iterator_category; //注意 单向__slist_node_base* node; //指向节点的基本结构 next指针__slist_iterator_base(__slist_node_base* x): node(x){}void incr(){node = node->next; } //前进一个节点bool operator==(const __slist_iterator_base& x)const{return node == x.node;}bool operator!=(const __slist_iterator_base& x)const{return node != x.node;}
};//单向链表的迭代器结构
template <class T,class Ref,class Ptr>
struct __slist_iterator : public __slist_iterator_base{typedef __slist_iterator<T,T&,T*>  iterator;typedef __slist_iterator<T,const T&,const T*>const_iterator;typedef __slist_iterator<T,T&,T*>  self;typedef T value_type;typedef Ptr pointer;typedef Ref reference;typedef __slist_node<T> list_node;//调用slist<T>::end()会造成__slist_iterator(0) 于是调用如下函数__slist_iterator(list_node* x) : __slist_iterator_base(x){}__slist_iterator(): __slist_iterator_base(0){}__slist_iterator(const iterator& x): __slist_iterator_base(x.node){}reference operator*() const {return ((list_node*)node)->data;}reference operator->() const {return &(operator*());}self& operator++(){incr(); //前进一个节点return *this;}self& operator++(int){self tmp = *this;incr(); //前进一个节点return tmp;}//没有实现operator-- 因为这是一个单向链表,使用的是forward iterator
};
  • 注意比较两个slist迭代器是否等同时,比如常规循环下需要判断 迭代器是否等同于 slist.end()
  • 但是__slist_iterator并没有对operator==进行重载,所以会调用 __slist_iterator_base::operator== ,__slist_iterator_base::operator==内部通过判定 __slist_iterator_base* node来判定两个迭代器是否等同

slist的数据结构

#include <iostream>//单向链表的节点的基本结构
struct __slist_node_base{__slist_node_base* next;
};//单向链表的节点结构
template <class T>
struct __slist_node : public __slist_node_base{T data;
};//全局函数 已知某一个节点,插入新的节点于其后
inline __slist_node_base* __slist_make_link(__slist_node_base* prev_node,__slist_node_base* new_node){//令new_node节点的下一个节点为prev节点的下一节点new_node->next = prev_node->next->next;prev_node->next = new_node; //令prev节点的next指针指向new节点return new_node;
}//全局函数 单向链表的大小(元素的个数)
inline std::size_t __slist_size(__slist_node_base* node){std::size_t result = 0;for( ; node != 0;node = node->next){++result; //一个一个累计}return result;
}//单向链表的迭代器的基本结构
struct __slist_iterator_base{typedef std::size_t size_type;typedef std::ptrdiff_t difference_type;typedef std::forward_iterator_tag iterator_category; //注意 单向__slist_node_base* node; //指向节点的基本结构 next指针__slist_iterator_base(__slist_node_base* x): node(x){}void incr(){node = node->next; } //前进一个节点bool operator==(const __slist_iterator_base& x)const{return node == x.node;}bool operator!=(const __slist_iterator_base& x)const{return node != x.node;}
};//单向链表的迭代器结构
template <class T,class Ref,class Ptr>
struct __slist_iterator : public __slist_iterator_base{typedef __slist_iterator<T,T&,T*>  iterator;typedef __slist_iterator<T,const T&,const T*>const_iterator;typedef __slist_iterator<T,T&,T*>  self;typedef T value_type;typedef Ptr pointer;typedef Ref reference;typedef __slist_node<T> list_node;//调用slist<T>::end()会造成__slist_iterator(0) 于是调用如下函数__slist_iterator(list_node* x) : __slist_iterator_base(x){}__slist_iterator(): __slist_iterator_base(0){}__slist_iterator(const iterator& x): __slist_iterator_base(x.node){}reference operator*() const {return ((list_node*)node)->data;}reference operator->() const {return &(operator*());}self& operator++(){incr(); //前进一个节点return *this;}self& operator++(int){self tmp = *this;incr(); //前进一个节点return tmp;}//没有实现operator-- 因为这是一个单向链表,使用的是forward iterator
};template<class T,class Alloc>
class simple_alloc{
public:static T* allocate(std::size_t n){return 0==n?0:(T*)Alloc::allocate(n * sizeof(T));}static T* allocate(void){return (T*)Alloc::allocate(sizeof (T));}static void deallocate(T* p,size_t n){if (n!=0){Alloc::deallocate(p,n * sizeof(T));}}static void deallocate(T* p){Alloc::deallocate(p,sizeof(T));}
};#ifdef __STL_USE_EXCEPTIONS
#define __STL_TRY   try
#define __STL_UNWIND(action)   catch(...) { action; throw; }
#else
#define __STL_TRY
#define __STL_UNWIND(action)
#endifnamespace Chy{template <class T>inline T* _allocate(ptrdiff_t size,T*){std::set_new_handler(0);T* tmp = (T*)(::operator new((std::size_t)(size * sizeof (T))));if (tmp == 0){std::cerr << "out of memory" << std::endl;exit(1);}return tmp;}template<class T>inline void _deallocate(T* buffer){::operator delete (buffer);}template<class T1,class T2>inline void _construct(T1 *p,const T2& value){new(p) T1 (value);  //没看懂}template <class T>inline void _destroy(T* ptr){ptr->~T();}template <class T>class allocator{public:typedef T           value_type;typedef T*          pointer;typedef const T*    const_pointer;typedef T&          reference;typedef const T&    const_reference;typedef std::size_t size_type;typedef ptrdiff_t   difference_type;template<class U>struct rebind{typedef allocator<U>other;};pointer allocate(size_type n,const void * hint = 0){return _allocate((difference_type)n,(pointer)0);}void deallocate(pointer p,size_type n){_deallocate(p);}void construct(pointer p,const T& value){_construct(p,value);}void destroy(pointer p){_destroy(p);}pointer address(reference x){return (pointer)&x;}const_pointer const_address(const_reference x){return (const_pointer)&x;}size_type max_size()const{return size_type(UINT_MAX/sizeof (T));}};
}template <class T,class Alloc>
class slist{
public:typedef T value_type;typedef value_type* pointer;typedef const value_type* const_pointer;typedef value_type& reference;typedef const value_type& const_reference;typedef std::size_t size_type;typedef std::ptrdiff_t difference_type;typedef __slist_iterator<T,T&,T*> iterator;typedef __slist_iterator<T,const T&,const T*>const_iterator;
private:typedef __slist_node<T> list_node;typedef __slist_node_base list_node_base;typedef __slist_iterator_base iterator_base;typedef simple_alloc<list_node,Alloc>list_node_allocator;static list_node* create_node(const value_type& x){list_node* node = list_node_allocator::allocate(); //配置空间__STL_TRY{Chy::allocator<T>::construct(&node->data,x);node->next = 0;};__STL_UNWIND(list_node_allocator::deallocate(node);) //释放空间}static void destroy_node(list_node* node){Chy::allocator<T>::destroy(&node->data);//将元素析构list_node_allocator::deallocate(node);  //释放空间}
private:list_node_base head; //头部 注意head不是指针,是事物
public:slist(){head.next = 0;}~slist(){clear();}
public:void clear(){erase_after(&head,0);}//全局函数 传递链表的头部和尾部inline list_node_base* erase_after(list_node_base* before_first,list_node_base* last_node){list_node * cur = (list_node*)(before_first->next);while (cur != last_node){list_node * tmp = cur;cur = (list_node *)cur->next;destroy_node(tmp);}before_first->next = last_node;return last_node;}
public:iterator begin(){return iterator((list_node*)head.next);}iterator end(){return iterator(0);}size_type size() const{return __slist_size(head.next);}bool empty() const {return head.next == 0;}//两个slist之间互换,只需要交换head头结点即可void swap(slist& L){list_node_base* tmp = head.next;head.next = L.head.next;L.head.next = tmp;}//获取头部元素reference front(){return ((list_node*)head.next)->data;}//从头部插入元素 新的元素成为slist的第一个元素void push_front(const value_type& x){__slist_make_link(&head, create_node(x));}//注意 没有push_back()//从头部取出元素,将其删除之 修改headvoid pop_front(){list_node * node = (list_node*)head.next;head.next = node->next;destroy_node(node);}};

元素操作

参考链接

  • STL源码剖析(十二)序列式容器之slist_JT同学的博客-CSDN博客

STL源码剖析 slist单向链表概述相关推荐

  1. STL源码剖析 stack 栈 概述->(使用deque双端队列 / list链表)作为stack的底层容器

    Stack是一种先进后出的数据结构,他只有一个出口 stack允许 新增元素.移除元素.取得最顶端的元素,但是无法获得stack的内部数据,因此satck没有遍历行为 Stack定义的完整列表 (双端 ...

  2. C++ STL源码剖析 笔记

    写在前面 记录一下<C++ STL源码剖析>中的要点. 一.STL六大组件 容器(container): 各种数据结构,用于存放数据: class template 类泛型: 如vecto ...

  3. STL(C++标准库,体系结构及其内核分析)(STL源码剖析)(更新完毕)

    文章目录 介绍 Level 0:使用C++标准库 0 STL六大部件 0.1 六大部件之间的关系 0.2 复杂度 0.3 容器是前闭后开(左闭右开)区间 1 容器的结构与分类 1.1 使用容器Arra ...

  4. 【STL源码剖析】list模拟实现 | 适配器实现反向迭代器【超详细的底层算法解释】

    今天博主继续带来STL源码剖析专栏的第三篇博客了! 今天带来list的模拟实现! 话不多说,直接进入我们今天的内容! 前言 那么这里博主先安利一下一些干货满满的专栏啦! 手撕数据结构https://b ...

  5. STL源码剖析学习二:空间配置器(allocator)

    STL源码剖析学习二:空间配置器(allocator) 标准接口: vlaue_type pointer const_pointer reference const_reference size_ty ...

  6. STL源码剖析 空间配置器 查漏补缺

    ptrdiff_t含义 减去两个指针的结果的带符号整数类型 ptrdiff_t (Type support) - C 中文开发手册 - 开发者手册 - 云+社区 - 腾讯云 std::set_new_ ...

  7. 《STL源码剖析》相关面试题总结

    一.STL简介 STL提供六大组件,彼此可以组合套用: 容器 容器就是各种数据结构,我就不多说,看看下面这张图回忆一下就好了,从实现角度看,STL容器是一种class template. 算法 各种常 ...

  8. STL源码剖析(一)STL简介

    STL源码剖析(一)STL简介 文章目录 STL源码剖析(一)STL简介 一.STL概述 二.STL六大组件 2.1 容器(containers) 2.2 算法(algorithms) 2.3 迭代器 ...

  9. 《STL源码剖析》笔记——allocator

    六大组件间关系 部分STL文件包含关系 allocator包含于中: 实际实现于三个文件 : 1.stl_construct.h :对象的构造和析构 2.stl_alloc.h空间配置和释放 3.st ...

最新文章

  1. spring aop xml事务配置
  2. nagios自定义监控API插件
  3. [C++程序语言设计笔记一]面向对象编程抽象,继承,重写基本介绍
  4. boost::contract模块实现lambda表达式的测试程序
  5. 15个优雅的Python编程技巧,掌握后瞬间玩转Python
  6. spring AOP注解实现
  7. (34)System Verilog引用包中定义的类(失败)
  8. Caffe各版本与源码全透析
  9. Linux的shell中echo改变输出显示样式
  10. C++ Windows键盘钩子
  11. html如何设置自动点击事件,html 如何设置打开网页时自动触发按钮的单击事件
  12. 小米html查看器 联网,小米摄像机如何连接无线路由器通过手机查看
  13. 【异常】git提示Ask a project Owner or Maintainer to create a default branch
  14. php股票网站源代码,基于php的新三板实时股票信息接口调用代码实例
  15. Incorrect string value: ‘\\xB2\\xE9\\xD1\\xAF\\xCA\\xD3...‘ for column ‘name‘ at row 1
  16. 用js做购物界面的常用效果
  17. Qt 快速入门系列教程
  18. uniapp 全局方法封装
  19. 10个挖掘edusrc常用工具、脚本
  20. 孙鑫java基础视频教程_孙鑫老师JAVA无难事视频教程 最适合java入门学习打基础的课程 附源码讲义 12课...

热门文章

  1. maven springboot 除去指定的jar包_SpringBoot的运行机制
  2. 【转】05.Dicom 学习笔记-DICOM C-Echo 消息服务
  3. .netcore持续集成测试篇之MVC层单元测试
  4. 用友通ERP客户端报无法登陆错
  5. Pandas数据可视化工具:图表工具-Seaborn
  6. matplotlib的优点_超详细matplotlib基础介绍!!!
  7. java环境_配置java环境变量
  8. ffmpeg录屏鼠标闪烁问题解决方法
  9. 数据结构 - 赫夫曼树
  10. 【情人节表白代码】——情人节将至,10余款浪漫爱心特效送给你爱的那个她