STL源码分析之slist有序容器 上

【单链表接口实现-C语言】 单链表接口实现-C语言 阅读详情

前言

不同于list是Forward Iterator类型的双向链表, slist是单向链表, 也是Bidirectional Iterator类型. slist主要耗费的空间小, 操作一些特定的操作更加的快, 同样类似与slist, 在执行插入和删除操作迭代器都不会像vector使迭代器失效. slist主要的问题在于它是单向的, 正向迭代器, 只有push_front, 没有push_back的操作, 指定位置之后的插入和删除时间都是O(1), 而在指定位置之前插入就需要重新遍历链表时间就是O(n), 效率很低.

slist源码

slist的构成与list还是异曲同工.

节点结构

将节点的每一部分都进行剥离, 节点是一个结构构成, 数据是继承节点.

struct __slist_node_base
{
  __slist_node_base* next;	// 只有指向下一个节点的指针
};
// 继承节点指针, __slist_node增加数据
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->next = prev_node->next;
  prev_node->next = new_node;
  return new_node;
}

寻找指定节点的prev节点. 因为slist是正向迭代器就只有遍历链表

inline __slist_node_base* __slist_previous(__slist_node_base* head, const __slist_node_base* node)
{
    // 遍历整个链表来寻找
  while (head && head->next != node)
    head = head->next;
  return head;
}
inline const __slist_node_base* __slist_previous(const __slist_node_base* head,const __slist_node_base* node)
{
    // 遍历整个链表来寻找
  while (head && head->next != node)
    head = head->next;
  return head;
}

将指定范围的链表剪切到pos链表后

inline void __slist_splice_after(__slist_node_base* pos,__slist_node_base* before_first, __slist_node_base* before_last)
{
  if (pos != before_first && pos != before_last) {
    __slist_node_base* first = before_first->next;
    __slist_node_base* after = pos->next;
      // 将before_first之后的调整为before_last后面的链表
    before_first->next = before_last->next;
      // 将[before_first->next, before_last]插入到pos之后
    pos->next = first;
    before_last->next = after;
  }
}

将链表进行倒转

inline __slist_node_base* __slist_reverse(__slist_node_base* node)
{
  __slist_node_base* result = node;
  node = node->next;
  result->next = 0;
  while(node) {
      // 将原链表一一的插入到临时链表的尾, 实现链表的元素倒转
    __slist_node_base* next = node->next;
    node->next = result;
    result = node;
    node = next;
  }
  return result;
}
迭代器

之前说过slist的迭代器是正向的, 现在就来具体分析.

__slist_iterator_base单向链表的迭代器基本结构

struct __slist_iterator_base
{
    // 定义类型
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;
  typedef forward_iterator_tag iterator_category;
	// 定义节点指针
  __slist_node_base* node;
	// 构造函数
  __slist_iterator_base(__slist_node_base* x) : node(x) {}
    // incr: 只正向.
  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;
  }
};

__slist_iterator单向链表迭代器结构. 只重载实现了++操作, 没有–, 因为就只是正向迭代, --操作就需要重新遍历链表.

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, Ref, Ptr>           self;
	// 满足traits编程
  typedef T value_type;
  typedef Ptr pointer;
  typedef Ref reference;
  	// 定义链表节点
  typedef __slist_node<T> list_node;
	// 构造函数
  __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; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
  pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */

	// 对++重载
  self& operator++()
  {
    incr(); // 指向下一个节点
    return *this;
  }
  self operator++(int)
  {
    self tmp = *this;
    incr();
    return tmp;
  }
};

#ifndef __STL_CLASS_PARTIAL_SPECIALIZATION
inline ptrdiff_t* distance_type(const __slist_iterator_base&)
{
  return 0;
}
inline forward_iterator_tag iterator_category(const __slist_iterator_base&)
{
  return forward_iterator_tag();
}
template <class T, class Ref, class Ptr> 
inline T*  value_type(const __slist_iterator<T, Ref, Ptr>&) {
  return 0;
}
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */

通过遍历整个链表计算链表的长度.

inline size_t __slist_size(__slist_node_base* node)
{
  size_t result = 0;
  for ( ; node != 0; node = node->next)
    ++result;
  return result;
}
slist分析

slist类型定义.

template <class T, class Alloc = alloc>
class slist
{
public:
    // 满足traits编程
  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 size_t size_type;
  typedef 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;	// 定义空间配置器

private:
  list_node_base head;	// 定义节点的头
    ...	
};
slist构造函数
template <class T, class Alloc = alloc>
class slist
{
	...
public:
  slist() { head.next = 0; }	// 默认构造函数, head.next == 0

    // 以下构造函数都调用fill_initialize填充链表
  slist(size_type n, const value_type& x) { fill_initialize(n, x); }
  slist(int n, const value_type& x) { fill_initialize(n, x); }
  slist(long n, const value_type& x) { fill_initialize(n, x); }
  explicit slist(size_type n) { fill_initialize(n, value_type()); }	// 不能隐式调用该构造函数

#ifdef __STL_MEMBER_TEMPLATES
    // 接受两个迭代器, 范围初始化
  template <class InputIterator>
  slist(InputIterator first, InputIterator last) {
    range_initialize(first, last);
  }
#else /* __STL_MEMBER_TEMPLATES */
  slist(const_iterator first, const_iterator last) {
    range_initialize(first, last);
  }
  slist(const value_type* first, const value_type* last) {
    range_initialize(first, last);
  }
#endif /* __STL_MEMBER_TEMPLATES */
	...
};

fill_initialize, 初始化

void fill_initialize(size_type n, const value_type& x) {
    head.next = 0;
    __STL_TRY {
      _insert_after_fill(&head, n, x);
    }
    __STL_UNWIND(clear());
}

range_initialize, 自定义范围初始化

template <class T, class Alloc = alloc>
class slist
{
	...
private:
#ifdef __STL_MEMBER_TEMPLATES
  template <class InputIterator>
  void range_initialize(InputIterator first, InputIterator last) {
    head.next = 0;
    __STL_TRY {
      _insert_after_range(&head, first, last);
    }
    __STL_UNWIND(clear());
  }
#else /* __STL_MEMBER_TEMPLATES */
  void range_initialize(const value_type* first, const value_type* last) {
    head.next = 0;
    __STL_TRY {
      _insert_after_range(&head, first, last);
    }
    __STL_UNWIND(clear());
  }
  void range_initialize(const_iterator first, const_iterator last) {
    head.next = 0;
    __STL_TRY {
      _insert_after_range(&head, first, last);
    }
    __STL_UNWIND(clear());
  }
#endif /* __STL_MEMBER_TEMPLATES */
	...
};

range_initialize和fill_initialize函数都是调用_insert_after_range, _insert_after_range也是有多个重载函数.

_insert_after_range, 初始化成员属性

template <class T, class Alloc = alloc>
class slist
{
	...
private:
  void _insert_after_fill(list_node_base* pos,
                          size_type n, const value_type& x) {
      // 构造出n个节点并初始化为x
    for (size_type i = 0; i < n; ++i)
      pos = __slist_make_link(pos, create_node(x));
  }

#ifdef __STL_MEMBER_TEMPLATES
  template <class InIter>
  void _insert_after_range(list_node_base* pos, InIter first, InIter last) {
      // 构造出 [first, last) 个节点并初始化为first
    while (first != last) {
      pos = __slist_make_link(pos, create_node(*first));
      ++first;
    }
  }
#else /* __STL_MEMBER_TEMPLATES */
  void _insert_after_range(list_node_base* pos,
                           const_iterator first, const_iterator last) {
      // 构造出 [first, last) 个节点并初始化为first
    while (first != last) {
      pos = __slist_make_link(pos, create_node(*first));
      ++first;
    }
  }
  void _insert_after_range(list_node_base* pos,
                           const value_type* first, const value_type* last) {
      // 构造出 [first, last) 个节点并初始化为first
    while (first != last) {
      pos = __slist_make_link(pos, create_node(*first));
      ++first;
    }
  }
#endif /* __STL_MEMBER_TEMPLATES */
    ...
};

create_node函数, 调用空间配置器分配空间然后在调用构造函数

template <class T, class Alloc = alloc>
class slist
{
	...
private:
  static list_node* create_node(const value_type& x) {
      // 空间配置器分配空间
    list_node* node = list_node_allocator::allocate();
    __STL_TRY {
        // 调用构造函数
      construct(&node->data, x);
      node->next = 0;
    }
      // 如果分配失败就释放掉之前分配的所有空间
    __STL_UNWIND(list_node_allocator::deallocate(node));
    return node;
  }
	...
};
析构函数
template <class T, class Alloc = alloc>
class slist
{
	...
private:
    ~slist() { clear(); }	// 调用clear函数将所有元素进行删除和释放
    ...
};

destroy_node函数, 调用析构函数之后再释放空间

template <class T, class Alloc = alloc>
class slist
{
	...
private:
  static void destroy_node(list_node* node) {
    destroy(&node->data);	// 调用析构函数
    list_node_allocator::deallocate(node);	// 释放内存空间
  }
	...
};
拷贝构造函数
template <class T, class Alloc = alloc>
class slist
{
	...
private:
    // 直接调用range_initialize函数进行初始化
  slist(const slist& L) { range_initialize(L.begin(), L.end()); }
    ...
};

总结

本节只是对slist的基本构成, 构造函数和析构函数做了一个探讨, 下一节就准备分析slist的删除, 插入等具体操作的实现.

slist源码 slist是一个单向列表 相比与双向列表所耗用的空间更小,某些操作更快 根据STL习惯,插入操作会将新元素插入指定位置之前 而非之后。然后作为一个单向链表,slist没有任何方便的办法可以回头定出前一个位置,因此 他必须从头找起。换句话说,除了slist起点附近的点之外,其他位置上采用inset和erase操作函数,都不是一种好的选择 为了克服这个缺点 slist提供了insert_after()... 阅读详情

相关推荐

c++ || STL序列容器

STL 、vector、list、deque、

LQEmptycity的博客 683

xlist

package a16.erin.unit14_xlist_http; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import com.google.gson.Gso

sweetliteli的博客 4059

深入解析SSD报错“READ FPDMA QUEUED”:从诊断到修复全流程

本文深入解析了SSD报错“READ FPDMA QUEUED”的含义、成因与全流程解决方案。该错误通常指示硬盘硬件或健康状态问题。文章详细介绍了如何通过系统日志定位错误、解读关键的S.M.A.R.T.健康报告(重点关注重分配扇区计数等指标),并逐步进行硬件排查(如更换SATA数据线)、软件修复与数据抢救,最终指导用户根据诊断结果做出维修或更换SSD的决策。

weixin_29233333的博客 397

slist

slist STL list是个双向链表。SGI STL另提供了一个单向链表,名为slist。 slist和list的主要差别在于,前者的迭代器属于单向的 Forward Iterator,后者的迭代器属于双向的Bidirectional Iterator。单向链表所耗用的空间更小,某些操作更快。 slist和list共同具有的一个相同特色是,它们的插入,移除,接合操作并不会造成原有的迭代器失效(当然了,指向被移除元素的那个迭代器,在移除操作发生之后肯定是会失效的)。 注意,根据STL的习惯,插入操作会将新

qq_36553387的博客 2817

使用protege推理小例子---关系链

使用protege推理小例子---关系链关系链推理 SubPropertityChain在Protege中使用关系链推理1、创建Person类2、创建对象关系3、添加实例4、进行推理5、推理结果 关系链推理 SubPropertityChain 关系链简单的说就是将多个关系表达式串联起来,如下图 1-1 图1-1 小明的父亲是大明,大明的父亲是老明。这就是一个由两组关系连接的关系链,根据关系链可以推理出,小明和老明之间的关系是爷孙关系。 在Protege中使用关系链推理 1、创建Person类 在Entit

weixin_45237780的博客 4457

Arduino读取串口接收到的数据并发送数据

Arduino读取串口接收到的数据并发送数据 源程序: void setup() { Serial.begin(115200); } void loop() { String comdata ; while (Serial.available() > 0) { comdata += char(Serial.read()); delay(2); } if (comdata.length() > 0) { Serial.print("串口接收到的数据:"); Serial.println(comd

perseverance51博客 1万+

单链表(SList)实现(有详细注释)

三个文件 还在更新 SList.h #pragma once #include <stdio.h> #include <stdlib.h> typedef int SListDataType; typedef struct SListNode { SListDataType data; struct SListNode* next; }SListNode; //尾部插入 void SListPushBack(SListNode** pphead, SListDataTyp

_zhy_cq 1913

实战|2026美团&大众点评餐饮数据抓取:Playwright/Selenium反爬全攻略

核心结论:2026年美团&大众点评的反爬核心是“多维度风控”,需组合使用“住宅IP+设备指纹伪装+真人行为模拟+动态验证处理”,Playwright是最优工具,Selenium仅作为补充。效率优化:单机爬取速度建议控制在“每小时500-800条”,避免高频请求触发风控;分布式爬取需确保每个节点使用独立住宅IP,且行为轨迹不一致。扩展方向集成WASM解密工具,破解2026年美团的Token加密;用AI识别动态点选验证,提升验证通过率;对接大数据平台,对抓取的餐饮数据进行可视化分析。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 2461

XListView的使用

  //XListView主要是要实现条目的上拉加载和下拉刷新的功能 在使用的时候需要导入一个Module包(可以自己手写也可以向小编留言私发) 因为解析时需要网络权限和图片的解析等等,所以记得加权限和依赖等操作,准备就绪开始上代码 1.首先布局-------宽高ID无所谓但必须是xlistview.bawei.com.xlistviewlibrary.XListView这个控件 &l...

H_BuilDer的博客 1847

YOLOv11改进策略【Neck】| arXiv 2025 分层多头注意力HMHA 子空间拆分提多样 + 通道重排降冗余,强化目标检测精度

HMHA(Hierarchical Multi-Head Attention,分层多头注意力)旨在解决传统多头注意力(MHA)在图像恢复任务中的核心缺陷——特征冗余。传统MHA将通道均匀拆分给各个头,导致不同头学习的子空间信息相似,进而出现多头关注同一区域、忽略退化区域修复的问题,限制了模型的特征表达能力。HMHA通过“分层子空间拆分+通道重排序”策略,让每个头学习差异化、互补的上下文特征,从根源上缓解冗余,提升图像恢复任务中对复杂退化场景的特征捕捉能力。

Limiiiing的博客 778

Headless Browser 反检测:如何在自动化测试和爬虫中模拟真实用户环境和指纹,以规避检测?

这是最基本的操作,你需要将 Headless Browser 的 User-Agent 修改成一个真实的 User-Agent。我是你们的老朋友,代码界的段子手,今天咱们来聊聊一个让爬虫工程师和自动化测试工程师头疼,但又不得不面对的问题:Headless Browser 的反检测。Headless Browser 的反检测是一个持续对抗的过程,网站的反爬虫机制会不断升级,你需要不断地学习新的反检测技巧。以上只是一些基本的反检测手段,如果你的目标网站的反爬虫机制比较高级,那么你需要使用更高级的技巧。

weixin_41455464的博客 1657

STL源码分析之slist有序容器 下

前言 上节我们对slist的基本构成, 构造析构做了分析, 本节 我们就来分析关于slist的基本元素操作. slist分析 基本属性信息 slist是只有正向迭代, 所以只能直接获取头部的数据. template &lt;class T, class Alloc = alloc&gt; class slist { ... private: slist&amp; operator= (con...

啦啦啦 626

DFT笔记 DC/AC mode与Func

DFT scan可以分为DC和AC两种,区别如下图。

m0_61544122的博客 5718

slist的简单总结

一、slist的概述 STL list是个双向链表。SGI STL另提供了一个单向链表,叫做slist。如下图所示。这个容器并不在标准规范之内。 二、与list对比 不同之处: 1、slist和list的主要差别在于,前者的迭代器属于单向的Forward Iterator,后者的迭代器属于双向的Bidirectional Iterator。为此slist的功能自然也就受到了许多限制。但是单向链表...

Janway-Win 3328

YOLOv8进阶实战:融合SAHI超推理算法,在无人机应用中精准捕捉视频与图片中的微小目标

YOLOv8改进是一种先进的目标检测算法,通过引入辅助超推理算法SAHI推理,使得小目标无法逃避检测。SAHI推理算法结合视频和图片的特点,利用先进的超推理技术,提高了目标检测的准确性和鲁棒性。不仅如此,YOLOv8改进还支持对视频和图片进行实时的目标检测,将检测结果直观地呈现给用户。这一技术的应用范围广泛,可以用于安防领域,提升监控设备的性能和效率;也可以用于无人驾驶领域,实现精准的目标识别和跟踪。总之,YOLOv8改进是一项具有突破性的技术,为目标检测领域带来了重大的进展。

weixin_51988935的博客 806

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

xlistview的使用

//mian方法 import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import com.example.xlistview.Bean

xxb52306的博客 1133

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【数据结构入门】无头单向非循环链表(SList)详解(定义、增、删、查、改) | 图解链表,超生动详细哦~

文章目录(1)链表介绍1)为什么引入链表2)链表的概念及结构3)链表的分类(2)无头单向非循环链表的实现1)单链表的定义2)动态申请一个节点3)打印单链表4)单链表尾插5)单链表头插6)单链表尾删7)单链表头删8)在单链表中查找指定值的节点9)单链表在pos位置之后插入10)单链表删除指定pos位置的节点11)单链表删除指定pos位置之后的节点12)求单链表长度13)判断单链表是否为空12)求单链表长度13)判断单链表是否为空 学习链表之前,建议先学习下顺序表,配合这篇文章食用更佳哦: 【数据结构入门】顺序

codewinter的博客 3377

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Xlist

/** * @file XFooterView.java * @create Mar 31, 2012 9:33:43 PM * @author Maxwin * @description XListView's footer */ package com.example.day09_2; import android.content.Context; import android.

zh15001187361的博客 3782

C++之STL顺序容器

STL容器是一个通用的数据结构,可以处理不同数据类型,包含基本的数据结构如链表、堆栈、队列等。可以分为顺序容器、关联容器、 容器适配器、特殊容器。本篇博客将简要介绍一下STL容器中的顺序容器。

m0_74265792的博客 1283

C++学习之第十八天-STL有序关联式容器-set、map

关联式容器—set、multiset、map、multimap知识点总览 1.关联式容器: 底层实现使用红黑树。初始化、遍历、查找(count,find)、插入(insert)(set与map需要判断插入是不是成功),自定义类型需要去对Compare进行改写(std::less、std::greater、函数对象)。 2.set 1、不能存放关键字key相同的元素,关键字必须唯一 2、默认以升序进行排列 3、底层实现是红黑树--不支持进行数据修改,不支持下标访问 4...

weixin_49278191的博客 747

【STL四】序列容器——vector容器

vector 实现的是一个动态数组,即可以进行元素的插入和删除。

junxuezheng的博客 845

框架学习二:ListView的下拉刷新+上拉加载

一、XListView 1、下载 gitHub的地址。或者从这儿下。 2、用法

mwj_88的专栏 3万+

C++ STL中容器的使用全面总结

一、容器的定义        在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对像的指针,这种对象类型就叫做容器。很简单,容器就是保存其它对象的对象,当然这是一个朴素的理解,这种“对象”还包含了一系列处理“其它对象”的方法。 二、容器的种类   1、顺序容器:是一种各元素之间有顺序关系的线性表,是一种线性结构的可序群集。顺序性容器中的每个元素均有固定的位置,除非用删除或插入的操

算法改变人生 6万+

C++STL:无序关联容器、有序关联容器

文章目录一、无序关联容器二、有序关联容器 一、无序关联容器 无序关联容器: 二、有序关联容器 有序关联容器:

Rain的博客 2447

10单向链表(slist)

1、slist概述 slist并不在标准规格之内,sllist和list的主要差别在于,slist的迭代器属于单向的Forward Iterator,而list的迭代器属于双向的Bidirectional Iterator。由于slist没有任何方便的办法可以回头定出前一个位置,必须从头找起,所以对于slist除了起点外其他位置采用insert和erase操作都属于不智之举,这是slist相较于list的大缺点。为此,slist特别提供了insert_after()和erase_after()供灵活运用。

Revendell的博客 1595
上一篇: STL源码分析之priority_queue优先级队列
下一篇: STL源码分析之slist有序容器 下
Function_18
博客等级 码龄10年 193粉丝 239原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值