STL标准库与泛型编程(侯捷)笔记2

news2024/9/23 15:20:07

STL标准库与泛型编程(侯捷)

本文是学习笔记,仅供个人学习使用。如有侵权,请联系删除。

参考链接

Youbute: 侯捷-STL标准库与泛型编程

B站: 侯捷 - STL

Github:STL源码剖析中源码 https://github.com/SilverMaple/STLSourceCodeNote/tree/master

Github:课程ppt和源码 https://github.com/ZachL1/Bilibili-plus

下面是第二讲的部分笔记:C++标准库体系结构与内核分析(第二讲)

主要介绍分配器allocator,还有容器list的底层实现,讨论iterator traits的设计

文章目录

  • STL标准库与泛型编程(侯捷)
    • 8 源代码之分布 VCGcc
    • 9 OOP 面向对象编程 vs GP 泛型编程
    • 10 技术基础:操作符重载and模板泛化, 全特化, 偏特化
    • 11 分配器
    • 12 容器之间的实现关系与分类
    • 13 深度探索list(上)
    • 14 深度探索list(下)
    • 15 迭代器的设计原则和Iterator Traits的作用与设计
    • 后记

8 源代码之分布 VCGcc

学这门课应该有的基础:

C++基本语法

模板基础

数据结构和算法的基础

9 OOP 面向对象编程 vs GP 泛型编程

面向对象想要把data和method关联在一起,比如list类内部实现sort函数。

在这里插入图片描述

泛型编程想要把data和method分开来,比如vector和deque类没有实现sort函数,而是调用algorithm里面的sort函数,两者分开来。

在这里插入图片描述

10 技术基础:操作符重载and模板泛化, 全特化, 偏特化

这部分在侯捷老师的面向对象课程里有详细的介绍,请参考笔者的笔记,里面含有课程的视频链接。

C++面向对象高级编程(侯捷)笔记1

C++面向对象高级编程(侯捷)笔记2

C++程序设计兼谈对象模型(侯捷)笔记

这里补充type_traits里面用到的特化(specialization)的知识

泛化指的是用模板,特化指的是指定模板到具体的类型。下面的代码中__STL_TEMPLATE_NULL指的是 template<> 这种类型,表示空模板。

//type_traits.h文件
// 泛化
template <class _Tp>
struct __type_traits { 
   typedef __true_type     this_dummy_member_must_be_first;              
   typedef __false_type    has_trivial_default_constructor;
   typedef __false_type    has_trivial_copy_constructor;
   typedef __false_type    has_trivial_assignment_operator;
   typedef __false_type    has_trivial_destructor;
   typedef __false_type    is_POD_type;
};
// 特化
__STL_TEMPLATE_NULL struct __type_traits<int> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

__STL_TEMPLATE_NULL struct __type_traits<unsigned int> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

在这里插入图片描述

下图中的allocator这个类也是用到了泛化和特化,比如当allocator指定的是void类型的时候,会调用特化版本的代码。

在这里插入图片描述

偏特化:有多个模板参数,只指定部分个参数为特定类型。比如下图中的vector的类型T指定为bool,而后面的Alloc分配器没有指定类型,这就是一种偏特化。是一种个数的偏特化。

另外下图中iterator_traits中也用到了偏特化,只不过这里是对指针类型的一个偏特化,如果传入的是T*,会有一种处理方式。这种偏特化是范围的偏特化,指的是从T类型,范围缩到T *指针类型。

在这里插入图片描述

11 分配器

分配器 allocators

先谈operator new() 和malloc()

malloc的内存实际分配情况:

下图中的内存分配,蓝色是块是实际我们需要malloc给我们分配的空间,灰色的块是debug mode需要添加的内存空间,上下两个红色的块是cookie,它记录分配内存的大小(《深度探索c++对象模型》书上称为记录元素的大小,这个指的是delete时,该delete多大的内存空间,即delete掉的数组维度大小),绿色的部分指的是为了内存大小的对齐。

在这里插入图片描述

STL对allocator的使用

在这里插入图片描述

VC++6编译器所附的标准库

allocator分配内存的时候,调用operator new,而operator new调用c语言的malloc。

而释放内存的时候,调用operator delete,而operator delete调用c语言的free。

在这里插入图片描述

测试,分配512个int型数据:需要指定分配的大小,释放时也要指定大小

// VC的allocate第二个参数没有默认值,需要自己给定,任意值都行
int* p = allocator<int>().allocate(512, (int *)0); // allocator<int>()是一个临时对象
allocator<int>().deallocate(p, 512);

BC5编译器所附的标准库对allocator的使用

_RWSTD_COMPLEX_DEFAULT(a)就是a

所以可以看到下面的分配器用的就是allocator

在这里插入图片描述

那么BC5编译器对于allocator的内部设计呢?分配内存和回收内存用的还是operator new和operator delete,底部还是用c语言的malloc和free完成,和VC6的实现别无二致。

在这里插入图片描述

接着示范BC5分配器的使用

测试,分配512个int型数据:需要指定分配的大小,释放时也要指定大小

// BC的allocate第二个参数有默认值为0
int* p = allocator<int>().allocate(512); // allocator<int>()是一个临时对象
allocator<int>().deallocate(p, 512);

GNU C++(G++) 2.9版本所附的标准库

分配内存和回收内存用的还是operator new和operator delete,底部还是用c语言的malloc和free实现,和上面相同。

参见右下角的说明,这个分配器并没有被SGI STL header引入,即没有被使用。

在这里插入图片描述

那GNU C++2.9对allocator的使用是怎么样的呢?

它用的是一个名字叫做alloc的分配器,如下图所示。

在这里插入图片描述

G++2.9 alloc的实现如下图所示,共有16条链表,每一条链表负责特定大小的区块,编号为#0的链表负责大小为8B的内存块,编号为#1的链表负责大小为8 x 2 = 16B的内存块,编号为#3的链表负责大小为8 x 3 = 24B的内存块,…,以此类推,每次增加8B。每一条链表都是一次用malloc分配的一大块内存(带cookie),然后切分成小的标准块(不带cookie),当容器需要内存的时候,alloc就会按照需要的大小(变成8B的倍数)在链表中查找进行分配,这样实现在分配小块的时候就不需要存储cookie的开销了。

在这里插入图片描述

G++4.9所附的标准库,它的分配器实现

名字叫做new_allocator, 这个4.9版本默认又不再使用alloc分配器,而是继续调用operator new 和operator delete。

在这里插入图片描述

G++4.9所附的标准库中的__pool_alloc就是G++2.9版本的alloc分配器

在这里插入图片描述

如果想要使用这个分配器,语法是什么呢?

vector<string, __gnu_cxx::__pool_alloc<string>> vec;// __gnu_cxx是一个命名空间

12 容器之间的实现关系与分类

容器,结构与分类

下图中缩进的函数:下图中的rb_tree(红黑树)下面有4个set,map,multiset,multimap缩进,这表示下面四个和rb_tree是衍生关系(复合composition关系,has-a),拿set举例,表示set里面有(has a)1个rb_tree做底部,拿heap举例,表示heap里面有一个vector做底部,拿priority_queue举例,它是heap的缩进,表示priority_queue里面有一个heap作为底部,等等。

在这里插入图片描述

13 深度探索list(上)

容器list

容器list是双向链表:底部实现是环状双向链表

下面是以GNU C++2.9版本进行介绍

list内部除了存data之外,还要存一个前向指针prev和一个后向指针next。

list的iterator,当迭代器++的时候,是从一个节点走到下一个节点,是通过访问next指针实现的。

template<class T>
struct __list_node {
    // 设计不理想,后面需要类型转型
    typedef void* pointer; // 这里__list_node中竟然有一种指向void的指针,而不是指向自己类型的指针
    void_pointer prev;
    void_pointer next;
    T data;
};

template<class T, class Alloc = alloc>
class list {
protected:
    typedef __list_node<T> list_node;
public:
    typdef list_node* link_node;
 	typedef __list_iterator<T, T&, T*> iterator;
protected:
    link_type node;
...
};

template<class T, class Ref,  class Ptr>
struct __list_iterator {
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
...
};

在这里插入图片描述

list’s iterator

下面看list的迭代器

__list_iterator 这个类中主要有两部分:一部分是一堆typedef,另一部分是操作符的重载,如下图所示。

在这里插入图片描述

看迭代器iterator,迭代器的++操作

++操作有两种,一种是++ite,另一种是ite++,分别是前置(prefix)和后置(postfix)

先看prefix form:通过取出next指针,指向链表的下一个节点,实现迭代器++的操作。

self&
operator++() // ++操作符重载
{
    // node是一个指向结构体__list_node的指针
    // 取出结构体里面的成员next指针
    node = (link_type)((*node).next); // link_type是指针类型
    return *this;
}
// 结构体如下所示
template<class T>
struct __list_node {
    typedef void* pointer; 
    void_pointer prev;
    void_pointer next;
    T data;
};

再看postfix form:用一个int参数operator++(int) 表示后++,当使用后置递增符号(如ite++)时,意味你想要递增迭代器,但在递增之前返回其先前的值。

self
operator++(int) //++操作符重载
{
    self tmp = *this; // 1.记录原值
    ++*this; // 2.前进一个位置
    //整个操作 ++*this 的结果是递增迭代器并返回递增后的迭代器的引用
    return tmp; // 3.返回原来的值
}

这里的 self tmp = *this;没有调用operator*这个操作,而是调用拷贝构造的函数,把后面*this解释为拷贝构造的参数

__list_iterator(const iterator& x): node(x.node) {}

再看这里的第2步++*this;,它也没有调用operator* 这个操作,而是因为++在前,调用前置++操作(进行node指向next指针,实现真正的迭代器前进),这里的*this被解释为operator++的参数。

*this 指向的是链表节点,通过 ++*this 可以使得链表迭代器前进到下一个节点。

在这里插入图片描述

template<class T,class Ref,class Ptr>
struct __list_iterator {
    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的节点
    
    //构造函数
    __list_iterator(link_type x):node(x) {}
    __list_iterator() {}
    __list_iterator(const iterator& x):node(x.node) {}
    
    reference operator*() const { return (*node).data;}
    pointer operator->() const {return &(operator*());}
    
    //前向迭代器
    self& operator++(){ // 前置++ite
        node = (link_type)((*node).next);
        return *this;
    }
    
    self operator++(int){ // 后置ite++
        self tmp = *this;
        ++*this;
        return tmp;
    }
    
    //后向迭代器
    self& operator--(){ // 前置--ite
        node=(link_type)((*node).prev);
        return *this;
    }
    
    self operator--(int){ // 后置ite--
        self tmp = *this;
        --*this;
        return tmp;
    }
};

这里的代码参考:https://www.cnblogs.com/ybf-yyj/p/9843391.html

14 深度探索list(下)

GNU C++2.9版本和GNU C++ 4.9版本关于list中iterator的设计差别

G2.9中iterator需要传三个模板参数<T, T&, T*>,而G4.9中仅需要传一个模板参数

在这里插入图片描述

G2.9中list的结构

在这里插入图片描述

G4.9中list的结构:继承关系更加复杂

在这里插入图片描述

15 迭代器的设计原则和Iterator Traits的作用与设计

Iterator需要遵循的原则

看一下rotate函数,它的参数里调用std::__iterator_category,里面返回iterator_category,这是iterator的一个属性,下面图还有另外两个属性,difference_typevalue_type,这里共涉及三个associated types(相关的类型),另外还有两种,分别是reference和pointer。也就是说共有5种associated types。

算法algorithm模块和容器container彼此独立,中间需要迭代器iterator进行交流

在这里插入图片描述

iterator必须提供的5种associated types,刚才上面介绍过,如下图所示:iterator_category, value_type, pointer, reference, difference_type。

difference指的是距离, 一个容器中两个iterator的距离

在这里插入图片描述

为什么需要traits?指针也被看作是一种退化的iterator,但指针并不是一个类,自然无法在类中定义上述的5种associated types。

traits机制必须有能力分辨它所获得的iterator是class iterator T还是native pointer to T(native pointer,原生指针,指的是non class(template) iterators,它无法定义associated type)

在这里插入图片描述

iterator traits用来分离class iterator和non-class iterator

在这里插入图片描述

当想知道一个迭代器的value type是什么的时候,不能直接I::value_type,而是

iterator_traits<I>::value_type

这就涉及到iterator_traits的偏特化(指针是范围上的偏特化,前文讲过),如果传入的是指针T*,它就是调用上图中的2或者3,直接定义T为value_type类型,如果传入的是class iterator,那就直接定义I::value_type为value_type。

完整的iterator_traits,这里就全部列出了5个asscicated types,以及偏特化处理传入的是指针的情况

在这里插入图片描述

后面还有各种类型的traits,比如type traits, char traits, allocator traits, pointer traits, array traits等等

后记

这是系列笔记连载,会继续更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1380887.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink窗口(2)—— Window API

目录 窗口分配器 时间窗口 计数窗口 全局窗口 窗口函数 增量聚合函数 全窗口函数&#xff08;full window functions&#xff09; 增量聚合和全窗口函数的结合使用 Window API 主要由两部分构成&#xff1a;窗口分配器&#xff08;Window Assigners&#xff09;和窗口函…

世邦spon IP网络对讲广播系统任意文件上传漏洞

产品介绍 世邦通信IP网络对讲广播系统采用领先的IPAudio™技术,将音频信号以数据包形式在局域网和广域网上进行传送,是一套纯数字传输系统。 漏洞描述 spon IP网络对讲广播系统存在任意文件上传漏洞&#xff0c;攻击者可以通过构造特殊请求包上传恶意后门文件&#xff0c;从…

软件测试|解决Github port 443 : Timed out连接超时的问题

前言 GitHub是全球最大的开源代码托管平台之一&#xff0c;许多开发者和团队使用它来管理和协作开源项目。但在当下&#xff0c;我们在clone或者提交代码时会经常遇到"GitHub Port 443: Timed Out"错误&#xff0c;这意味着我们的电脑无法建立与GitHub服务器的安全连…

UISegmentedControl控件定制

1.在xib中设计如下: 背景颜色: 段标题与数量 : 2.在代码中控制 关联控件 注册控件事件 控件事件处理函数实现: 定制Title颜色 4 --- > UIControlStateSelected 0 --- > UIControlStateNormal 最终实现效果: 取控件选中时的索引与显示文本: 输出:

理想汽车迎来新算力平台负责人,内部化名为张一粟;王者荣耀在抖音直播全面开放;陈楚生等人现身央视春晚彩排

今日精选 • 理想汽车迎来新算力平台负责人,内部化名为张一粟。目前理想内部暂未公布其内部职级• 王者荣耀在抖音直播全面开放• 陈楚生等人现身央视春晚彩排 投融资 • 2023年12月份&#xff0c;中国社会融资规模增量为1.94万亿元• OpenAI 支持的人形机器人公司 1X 完成 …

【C++】- 类和对象(构造函数!!explicit关键字stastic关键字!!详解)

类和对象④ 构造函数初始化列表explicit关键字static成员 构造函数初始化列表 我们已经初步了解了构造函数------->类和对象②那么调用构造函数就是给了对象中各个成员变量一个合适的初始值。 但实际上&#xff0c;我们想要做的是初始化成员变量&#xff0c;在构造函数中对…

【STM32】STM32学习笔记-FlyMCU串口下载和STLINK Utility(30)

00. 目录 文章目录 00. 目录01. 串口简介02. 串口连接电路图03. FlyMCU软件下载程序04. 串口下载原理05. FlyMCU软件其它操作06. STLINK Utility软件07. 软件下载08. 附录 01. 串口简介 串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式&#xff0c;因为它简…

【MySQL】MySQL表的约束-空属性/默认值/列属性/zerofill/主键/自增长/唯一键/外键

文章目录 表的约束1.空属性 --null && not null2.默认值 -- default3.列描述4.zerofill5.主键6.自增长7.唯一键8.外键 表的约束 表的约束&#xff1a;表中一定要有各种约束&#xff0c;通过约束&#xff0c;让我们未来插入数据库表中的数据是符合预期的。约束的本质是…

成就动机测试

成就动机测试广泛应用在职业发展领域&#xff0c;如&#xff1a;企业Hr人力资源管理部门&#xff0c;用于评估分析员工的潜能和价值&#xff0c;适用场景有人才招聘&#xff0c;岗位晋升&#xff0c;绩效考评等等。在大学生做职业规划&#xff0c;求职应聘中&#xff0c;应用成…

UG装配体组件重命名与导出组件

在一个装配文件中&#xff0c;如果我们想对其中一个零件的名称进行更改&#xff0c;可以打开单独文件然后另存为改名&#xff0c;或者直接改名后在装配体中进行替换&#xff0c;但是这样这样都是比较麻烦 我们可以使用零组件更名及导出命令 菜单-GC工具箱-GC数据规范-其他工具…

哪里能找到好用的PPT模板?12个免费模板网站让你畅快办公!

你是否有过这样的经历&#xff0c;在准备重要会议或者演讲的时候&#xff0c;为找不到合适的PPT模板而困扰&#xff1f;或是在网上漫无目的地搜寻&#xff0c;结果收获的是设计平淡无奇的PPT模板&#xff1f; 如果你有同样的疑问&#xff0c;那么你来对地方了&#xff01;在这…

PADS 改变图纸和图页边界大小

PADS 改变图纸和图页边界大小 有时候画一画原理图发现画布不够用了&#xff0c;可改变图纸大小&#xff0c;对应的改变图页边界 若图页边界怎么选择都改变不了&#xff0c;可将途中图页边界删除&#xff0c;重新加载 选择对应的图页边距就好啦 分类: PADS

【LeetCode:30. 串联所有单词的子串 | 滑动窗口 + 哈希表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

嵌套的CMake

hehedalinux:~/Linux/multi-v1$ tree . ├── calc │ ├── add.cpp │ ├── CMakeLists.txt │ ├── div.cpp │ ├── mult.cpp │ └── sub.cpp ├── CMakeLists.txt ├── include │ ├── calc.h │ └── sort.h ├── sort │ ├── …

(三)CMake为什么几乎一统C++跨平台构建?

先看几个简单的例子再回头来看这个问题 回想一下当我们用windows写C第一个Hello World!的步骤&#xff0c;先用VS IDE 创建一个控制台的工程&#xff0c;IDE 会自动生成一个 cpp 文件&#xff0c;里面有一句 输出"Hello World!" 代码&#xff0c;这个时候按下F5 就可…

如何生成文本: 通过 Transformers 用不同的解码方法生成文本

如何生成文本: 通过 Transformers 用不同的解码方法生成文本 假设 $p0.92$&#xff0c;Top-p 采样对单词概率进行降序排列并累加&#xff0c;然后选择概率和首次超过 $p92%$ 的单词集作为采样池&#xff0c;定义为 $V_{\text{top-p}}$。在 $t1$ 时 $V_{\text{top-p}}$ 有 9 个…

GULP 案例 4:如何计算热力学性质(热容、熵、焓、自由能等)?

---------------------------------------------------------------------- 物体的热力学性质是指物质处于平衡状态下压力 P、体积 V、温度 T、组成以及其他的热力学函数之间的变化规律。一般将材料的压力 P、体积 V、温度 T、内能 U、焓 H、熵 S 等统称为物体热力学性质。 热…

Android Lint的使用

代码检查方式一&#xff1a; Android Studio使用Lint进行代码检查 找到Analyze目录下的Inspect Code检查代码选项点击然后弹出下面这个框框&#xff0c;在这个列表选项中我们可以选择Inspect Code的范围&#xff0c;点击OK 待分析完毕后&#xff0c;我们可以在Inspection栏目中…

mysql数据库被黑恢复—应用层面delete删除---惜分飞

客户的mysql被人从应用层面攻击,并且删除了一些数据,导致业务无法正常使用,通过底层分析binlog确认类似恢复操作 确认这类的业务破坏是通过delete操作实现的,客户那边不太幸,客户找了多人进行恢复,现场严重破坏,老库被删除,并且还原了历史的备份文件(非故障第一现场),通过底层…

iOS Universal Links(通用链接)详细教程

一&#xff1a;Universal Links是用来做什么的&#xff1f; iOS9.0推出的用于应用之间跳转的一种机&#xff0c; 通过一个https的链接启动app。如果手机有安装需要启动的app&#xff0c;可实现无缝跳转。如果没有安装&#xff0c;会打开网页。 实现场景&#xff1a;微信链接无…