STL源码剖析_stl编程指令详解

STL源码剖析_stl编程指令详解SLT简介STL(StandardTemplateLibrary),即标准模板库,是一个高效的C++程序库。包含了诸多在计算机科学领域里常用的基本数据结构和基本算法。为广大C++程序员们提供了一

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

SLT简介

STL(Standard Template Library),即标准模板库,是一个高效的C++程序库。包含了诸多在计算机科学领域里常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性。其核心思想就是泛化编程(generic programming),在这种思想里,大部分基本算法被抽象,被泛化,独立于与之对应的数据结构,用于以相同或相近的方式处理各种不同情形。

STL组件

STL中包含了6大组件

  • 容器(Containers):包含各种基础的数据结构,如vector, list, deque, set, map等。
  • 分配器(Allocators):负责空间配置与管理。
  • 算法(Algorithms):各种常用的算法,如sort, search, copy, erase等等。
  • 迭代器(Iterators):负责连接Containers与Algorithms。
  • 适配器(Adapters):可以用来修饰Containers,Iterators和Functors接口的组件。
  • 函数式(Functors):类似于函数,可以作为Algorithms的一种策略。

六大组件的关系

Alt text
Containers 通过 Allocators 取得数据存储空间,Algorithms 通过 Iterators 存取 Containers 内容,Functors 可以协助 Algorithms 完成不同的策略变,Adapters 可以修饰或套接Containers,Iterators和Functors。

容器

结构与分类

容器总体上分为三大类:

  • Sequence Containers(序列容器): Arrary(大小固定,无法自动扩充), Vector(只可向后扩充, 两倍的扩展), Deque(可向前或向后扩充, 分段连续, stack和queue都是基于此 ), List(双向链表), Forwaed-List(单向链表)
  • Associative Containers(关联容器):Set/Multiset, Map/Multimap(基本都用红黑树来实现)
  • Unordered Containers(无序容器): Unordered Set/Multiset, Unordered Map/Multimap(基本都是 HashTable Separate Chaining 实现)

Alt text

Array

是一种固定大小的容器类型,在定义的时候就要声明大小和类型。Array其实就是对C语言中数组的一种扩充升级,使其支持了迭代器的操作,便于STL算法的使用。array在使用和性能上都要强于内置数组,对于一些固定大小的使用场景,可以用array来替代原先数组的工作。

TR1版本源码如下:

template<typename _Tp, std::size_t _Nm>
  struct array
  {
    typedef _Tp value_type;
    typedef _Tp* pointer;
    typedef balue_type* iterator;

    value_type _M_instance[_Nm ? _Nm : 1];

    iterator begin()
    { return iterator(&_M_instance[0]);}

    iterator end()
    { return iteratoe(&_M_instance[_Nm]);}

    ...
  };

Vector

Vector 使用起来和一个数组十分相似,但是在空间用完时,可以自动扩充自己的空间。一般而言空间的扩充,无法在原地完成扩充。所以会在内存中新申请一片内存(通常都是之前空间大小的2倍大),然后通过拷贝将原有数据拷贝到新的地址空间。

Vector中存在三个指针来表明Vector:

  • T* start:指向第一个元素的地址
  • T* finish:指向目前最后一个地址之后的一个空间的地址
  • T* end_of_storage:指向当前Vector的最后一个空间地址

需要注意的是:在空间(两倍)增长的过程中涉及到了大量的拷贝构造和析构!

List

相较于vector的连续线性空间,List就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间。因此,list对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插入或元素移除,List永远是常数时间。

List不仅是一个双向链表,而且还是一个环状双向链表。 另外,还有一个重要性质,插入操作和接合操作都不会造成原有的List迭代器失效,这在Vector是不成立的。因为Vector的插入操作可能造成空间的重新配置,导致原有的迭代器全部失效。甚至List的元素删除操作(erase),也只有“指向被删除元素”的那个迭代器失效,其他迭代器不受任何影响。

Forward-List

Forward-List容器与List容器的主要设计区别是List保持内部唯一的一个链接到下一个元素,而后者则保持每个元素的两个链接:一个指向下一个元素和一个前一个。允许高效在两个方向迭代,但每个元素的消耗额外的存储空间,并轻微较高的时间开销插入和删除元素的迭代。Forward-List对象,从而比List对象更有效率,虽然他们只能向前遍历。

所以Forward-List的一个最大的缺点就是无法直接访问指定位置上元素,每次一的访问都需要从头开始访问,这样的操作需要线型的时间。

Deque

Alt text

可以向两端扩充,通过指针连接不同分段的空间,模拟出连续空间。

template <class T, class Alloc=alloc, size_t BufSiz=0>
class deque{
public:
    typedef T value_type;
    typedef __deque_iterator<T,T&,T*,BufSiz> iterator;
protected:
    typedef pointer* map_pointer;//T**
protected:
    iterator start;
    iterator finish;
    map_pointer map;
    size_type map_size;
public:
    iterator begin(){return start;}
    iterator end(){return finish;}
    size_type size(){return finish-start;}
...
};

template <class T, class Ref, class Ptr, size_t BufSiz>
  struct __deque_iterator{
    typedef random_access_iterator_tag iterator_category;
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T** map_pointer;
    typedef __deque_iterator self;

    T* cur;
    T* first;
    T* last;
    map_pointer node;
    ...
};

start指向第一块分区,finishi指向最后一块分区,map是用来存放各个分区的地址(vector实现),map_size是map的大小。
start和finish作为iterator,cur指向当前的元素,first指向第一个存放的元素,last指向当前分区中最后一个存放的数据之后的位置,node指回map。

deque 如何模拟连续空间?

基本全部依靠deque iterators完成

reference operator*() const
{
    return *cur;
}

pointer operator->() const
{
   return &(operator*());
}

difference_type operator-(const self& x) const
{
    return difference_type(buff_size()) * (node - x.node - 1) + (cur - first) + (x.last - x.cur);
}

self& operator++(){
    ++cur;
    if(cur == last){
        set_node(node + 1);
        cur = first;
    }
    return *this;
}

self operator++(int){
    self tmp = *this;
    ++*this;
    return tmp;
}

self& operator--(){
    if(cur == first){
        set_node(node - 1);
        cur = last;
    }
    --cur;
    return *this;
}

self operator--(int){
    self tmp = *this;
    --*this;
    return tmp;
}

void set_node(map_pointer new_node){
    node = new_node;
    first = *new_node;
    last = first + difference_type(buffer_size());
}

self& operator+=(difference_type n){
    difference_type offset = n + (cur - first);
    if(offset >= 0 && offset < difference_type(buffer_size())){
        cur += n;
    }
    else{
        difference_type node_offset = offset > 0 ? offset / difference_type(buffer_size()) : -difference_type((-offset - 1) / buffer_size()) - 1;
        set_node(node + node_offset);
        cur = first + (offset - node_offset * difference_type(buffer_size()));
    }
    return * this;
}

self operator+(difference_type n) const {
    self tmp = *thisl
    return tmp +=n;
}

self& operator-=(fifference_type n)
{
    return *this += -n;
}

self operator-(difference_type n) const
{
    self tmp = * this;
    return tmp -= n;
}

reference operator[](difference_type n) const
{
    return *(*this + n);
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/167744.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 卷积神经网络CNN算法原理「建议收藏」

    卷积神经网络CNN算法原理「建议收藏」写在前面在上一篇【Deeplearning】卷积神经网络CNN结构中我们简单地介绍了CNN的结构。接下来我们看看这种结构的CNN模型是怎么运行的,包括CNN的前向传播和反向传播算法。1.CNN前向传播算法(1)输入层前向传播到卷积层输入层的前向传播是CNN前向传播算法的第一步。一般输入层对应的都是卷积层,因此我们标题是输入层前向传播到卷积层。我们这里还是以图像识别为例。先考虑…

    2025年8月28日
    7
  • c# 操作ad域用户

    c# 操作ad域用户测试环境:win2008r2服务器ad域服务器安装参考:https://www.cnblogs.com/cnjavahome/p/9029665.html密码策略修改参考:https://blog.csdn.net/zouyujie1127/article/details/40857675工作机dns设置为ad域服务器的ipusing:usingSystem.DirectoryServ…

    2022年5月16日
    122
  • Python 实现字符串反转的9种方法[通俗易懂]

    在做leetcode的试题中,做到反转整数,就涉及到字符串反转,为了尽可能可以写出更多的方法,于是写下这篇文章 样例:如a=’123456789′ 反转成a=’987654321’第一种方法:使用字符串切片&gt;&gt;&gt; a=’123456789′ &gt;&gt;&gt; a = a[::-1]’987654321’第二种方法:使用reversed()…

    2022年4月8日
    105
  • 创建线程池的方法_java引用就是对象本身

    创建线程池的方法_java引用就是对象本身创建线程池的方法一、创建线程池的三种方法Executors.newSingleThreadExecutor();//单个线程Executors.newFixedThreadPool(5);//创建一个固定的线程池Executors.newCachedThreadPool();//创建一个可伸缩的线程池1.newSingleThreadExecutorimportjava.util.concurrent.ExecutorService;importjava.util.concurr

    2022年9月30日
    2
  • VS2010编写动态链接库DLL和单元测试,转让DLL测试的正确性

    VS2010编写动态链接库DLL和单元测试,转让DLL测试的正确性

    2022年1月4日
    52
  • win10 虚拟显示器_电脑怎么设置虚拟显示器

    win10 虚拟显示器_电脑怎么设置虚拟显示器2017.7.7最近在做虚拟化,需要在虚机上虚拟出一个显示器,我使用的虚机是windows10,虚机里面有一张透传显卡(可看做是物理显卡),我尝试过一些方法,比如编写一个虚拟的WDDM显卡驱动,然后在显卡驱动上接上一个显示器,该方法是有效的,可以成功虚拟出一个显示器,但是在虚拟显示器上渲染数据使用的渲染引擎没有用到透传显卡,在性能上达不到我的要求,所以只好放弃用这种方法。于是,通过阅…

    2022年8月21日
    37

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号