自己动手实现简易STL

2023-10-27

之前学习C++看侯老师的书的时候实现了一下STL的基本组件,包括了6个组件,allocator, iterator, container, trait, functor, algorithm的组件,也是终于搞清楚了6个组件之间的相互关联.分享给大家。

STL六个组件

比较难理解的就是萃取器和迭代器还有算法之间的交互。其实就是一个算法库根据你迭代器属于不同类型进行的一个选择,选择相对最优的方法。下面展示一下核心部分。对应于STL的容器,分别是单向,双向,随机访问迭代器。

class input_iterator {};
class forward_iterator :public input_iterator {};
class bidirectional_iterator :public forward_iterator {};
class random_acess_iterator final: public bidirectional_iterator {};

用继承关系来表达这样一个迭代器的从属关系的原因是由于C++的一个机制,是利用重载的机制。我们首先考虑public继承关系是一个 is a 的关系,那么再这个层次的继承关系中,random_acess_iterator 是最为特殊的一个。
所以从特殊性的角度来考虑 (从特殊性来比较): random_acess_iterator > bidirectional_iterator > forward_iterator ,他们都是input_iterator。
C++的重载是有一个机制,如果有多个同时可以匹配的选择最特殊的那个,这个机制仔细想想也OK,既然我可以匹配更特殊的,何必匹配更加泛化的呢。
下面举一个最简单的算法例子就懂了。
计算两个迭代器之间的距离。

迭代器 & 算法

下面这个是调用函数,可以看到他还调用了一个_distance函数来作为辅助函数。

template<class InputIterator>
unsigned int distance(InputIterator first, InputIterator last)
{
	return _distance(first, last, Traits<InputIterator>::iterator_type());
}

辅助函数一:

template<class InputIterator>
unsigned int _distance(InputIterator first, InputIterator last, random_acess_iterator iter){
	return abs::abs(last - first);
}

第一行是为了debug的,后面一行可以看到只需要做一个减法就可以计算first到last的长度了。也就是O(1)的时间。为什么可以用减号,因为所有的随机访问迭代器都重载了 operator -
可以看到第三个参数就是做了这样一个选择。

辅助函数二:

template<class InputIterator>
unsigned int _distance(InputIterator first, InputIterator last, forward_iterator iter){
	unsigned int dis = 0;
	for (; first != last; ++first)
		++dis;
	return dis;
}

对于其他类型来说,我不能和随机访问迭代器一样,因此只能一个一个地加了,所以是O(last - first)的时间复杂度。

这里就体现了为什么需要迭代器,就是为了性能!

Q:为什么我不直接用这种重载的方式,非要加一个间接的调用层啊。
A: 因为容器有很多种,每个容器对应的迭代器实际是对应容器的特化的迭代器。但是这种重载机制需要迭代器的类型。而这个需要的类型是特化的那几种迭代器。
举个例子就是 deque和vector 都是random_acess_iterator.但是相应的算法具体是针对某个iterator。所以加入直接使用而不加这个distance的间接,是没有办法找到。或者你需要向里面一样复杂的写法,而又希望接口简单,隐藏细节,因此采用这种写法。

容器,迭代器部分

对于每个可以整迭代器的容器,都会有一个与之相当于的迭代器的,并且他们的关系是耦合的,也就是你实现一个容器,需要实现一个对应的迭代器才能加入STL的大家庭。当然也不是必须这么做,因为有的数据结构没法设计迭代器。能这么设计的好处就是你可以用标准库的算法。
下面简单列一下大概的结构。

template<class T>
class iterator{
	//用同意的来表示其数据或者容器的特性。
	using iterator_type = ...;
	...
	// 需要的重载,不多不少
	operator ++(){}
	operator ++(int){}
	operator != (const iterator&) {}
	...
};

template<class T, class Alloc = alloc>
class container{
public:
	// 表示其类型,方便萃取器trait来萃取类型。
	using value_type = T;
	using iterator = ...;
	...
	//函数实现 字段等等
	container();
	...
};

这样一说,就只有allocator没有说了,这里推荐看侯捷老师的内存管理,里面讲到了这种简单的实现,以及SGI的二层分配,也讲了一个loki的能够消除内部碎片的分配器。
这里为了简化,只是用了简单的new delete。
注意这里是不能使用 malloc 或者free的 除非你显示地调用对象析构函数。

class easy_allocator{
public:
	template<class T>
	static T* alloc(){
		return new T();
	}
	template<class T>
	static void dealloc(T* p){
		assert(p != nullptr);
		delete p;
	}
};

适配器

这个没有太多可以说的,你可以认为就是接口之间的转换。比如queue stack。举个例子就是queue 我需要先进先出 也就是push_back() & pop_front()两种操作, 因此我可以选择list deque。
那么对于stack来说 我需要push_back() & pop_back() 那么vector list deque都可以作为我的底层实现。你在使用标准库的时候也会看到你可以再模板自己定义一个类型。

stack<int, vector<int>> st;
queue<int, deque<int>> que;

仿函数

functor 就是为了让标准库的容器, 算法使用更加灵活。C++11之后很多接口可以直接用lambda 非常好用。

额外的工作

当时觉得这么写代码(CTRL + C + V)有点没有意思,于是当时想能不能稍微扩充个整个小玩具。于是封装win平台的窗口作为一个类, 实现了一个相当于一个 容器的 一个可视化。(并没有很好的完善,后面完善一下 贴出源代码。) 封装window窗口创建作为一个类 网络上可以查一下,有点点技巧。
使用方式类似与迭代器,因为是直接和容器相关的。类似于这样是直接耦合再一起

using window = window_vector<T>;

能够简单的可视化一些数据结构,也还是稍微有点意思吧。。。还增加的个按钮 可以看到你最近的操作是怎么变化的。

在这里插入图片描述
这个就是一个二叉树的啦。可以看到插入一个元素对他带来的变化。
红黑树 具体的变化:
红黑树就是不像AVL树对于高度那样严格,通过红黑节点的定义(有证明说明红黑树是和2-3-4树本质是一样的)
并且红黑树旋转的一个很重要性质就是: O(1)的旋转进行插入或者删除,对比AVL树是O(lgn)的旋转数。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这个是list的window…
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

而且用这个窗口可以很好的debug,比如树可以直观的看里面的东西对不对,print出来不太直观。
这些实现要注意窗口resize的消息,需要重新绘制一下。没有加入滚动条的功能,有时间再加。

小结

非常推荐去实现一个vector 包括所有的组件,其实也不是那么简单。不需要实现那么多算法 容器等,知道6个组件具体是怎么工作的就够了。
这里简洁一下6个组件如何交互,下篇文章会用最简单的vector举例子 示范一个具体的实现。

了解标准库,可以更好地扩展他。比如怎么使用它底层的分配器,容器等等。根据自己的需求进行选择,甚至可以做必要的扩充。

初学者,欢迎各位指出问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自己动手实现简易STL 的相关文章

随机推荐

  • Redis主从及哨兵模式配置教程

    提示 以下是本篇文章正文内容 下面案例可供参考 本文环境 CentOS7 3 Redis 5 0 7 一 Redis主从配置 1 主从搭建服务器情况 IP 角色 redis版本 192 168 223 131 主 Redis 5 0 7 1
  • C++判断输入结束的简单方法(从键盘输入+从文件读入)

    判断输入结束的简单方式 1 从键盘输入 1 最简单的方式 while cin gt gt a 当想结束时只需 换行 输入Ctrl Z 回车 此时cin gt gt a的返回值为false 例1 初始化字符数组 include
  • MySQL validatequery_Druid配置参数详解-validationQuery

    Druid配置参数详解 validationQuery Druid是一个由阿里开源的数据库连接池 Druid的配置非常丰富 但是设置不当会对生产环境造成严重影响 网上Druid的资料虽多 但大部分都是互相复制粘贴 有很多不准确甚至完全错误的
  • response.setContentType()的作用及参数

    response setContentType MIME 的作用是使客户端浏览器 区分不同种类的数据 并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据 例如web浏览器就是通过MIME类型来判断文件是GIF图片 通过MIM
  • SpringBoot 日志正确使用方式,这样才优雅!

    一 日志重要吗 程序中的日志重要吗 在回答这个问题前 笔者先说个事例 笔者印象尤深的就是去年某个同事 收到了客户反馈的紧急bug 尽管申请到了日志文件 但因为很多关键步骤没有打印日志 导致排查进度很慢 数个小时都没能排查到问题 也无法给出解
  • 基于单片机的七彩音乐喷泉设计

    目录 一 方案流程及技术规格书设计 二 系统硬件电路设计 三 软件编写及调试 四 系统调试测试与分析 前言 随着时代的进步 人们对生活质量的要求也在不断提升 因此 51单片机七彩音乐喷泉系统应运而生 它不仅可以满足人们对舒适环境的追求 而且
  • 安装mariadb启动报错

    报错如下 从这里并看不出什么端倪 7月 07 07 08 16 localhost localdomain mariadb prepare db dir 3287 Please check all of the above before s
  • MySQL添加字段和修改字段的方法

    原文地址 http database 51cto com art 201011 234549 htm MySQL添加字段的方法并不复杂 下面将为您详细介绍MySQL添加字段和修改字段等操作的实现方法 希望对您学习MySQL添加字段方面会有所
  • HBase建表函数createTable的几点说明

    HBase建表函数提供了四个重载函数 分别是 void createTable HTableDescriptor desc void createTable HTableDescriptor desc byte startKey byte
  • ctf.show web3

    打开题目 出现提示 php文件 考虑文件包含漏洞 输入参数 url etc passwd 这个报错界面出来了 说明存在文件包含漏洞 构造url值 php input 使用php协议 使用burp抓包 使用ls命令查看php下的文件 得到文件
  • 认认真真推荐几个高质量人工智能方向的优质原创公众号

    人工智能与计算机编程和数学相关性比较大 网络上的资料比较繁杂 想系统的学习人工智能谈何容易 今天给大家推荐9个原创公众号 这些公众号定期会发些高质量原创 希望可以让你更高效的学习 AI有道 一个值得关注的 AI 技术的公众号 作者红色石头是
  • 分布式session的4种解决方案

    分布式session的4种解决方案 1 cookie和session cookie和session都是用来跟踪用户身份信息的会话方式 cookie存储的数据保存在本地客户端 用户获取容易 但安全性不高 存储数据小 session存储的数据保
  • matlab plot三维图形

    偶尔 我们会用到三维图形 目前我所了解的matlab中有三种方式可以实现 分别是scatter plot3和meshgrid 具体用法如下 1 scatter x y z 其中x y z为同纬度的向量 生成的三维图是点的形式 2 x 1 0
  • Simulink单元测试

    本文使用Matlab2018a版本 一 主要使用Simulink中的Analysis下的Test Harness和Test Manager 1 创建Test Harness 前提 有测试模型 1 在测试模型里 直接右击 gt Test Ha
  • Ubuntu 18.04安装CUDA 11.4.0 cuDNN 8.2.2

    CUDA和cuDNN为NVIDIA支持GPU运算以及深度神经网络计算加速的算法库 通常需要安装以支持利用GPU加速神经网络的训练和推理 安装前需要确定主机显卡为NVIDIA显卡 且驱动安装无误 通过nvidia smi查看显卡信息和适合的C
  • 使用pyLDAvis可视化LDA结果,与解决FileNotFoundError: [Errno 2] No such file or directory: ‘https://cdn.jsdel....

    建议安装 pip install pyLDAvis 2 1 2 否则会报错 FileNotFoundError Errno 2 No such file or directory https cdn jsdelivr net gh bmab
  • java math类 平方_Java Math类

    首页 gt 基础教程 gt 常用类 gt 常用 Number Math类 Java Math类 Java 的 Math 包含了用于执行基本数学运算的属性和方法 如初等指数 对数 平方根和三角函数 这些方法基本可以分为三类 三角函数方法 指数
  • 深入理解Java虚拟机jvm-对象如何进入老年代

    HotSpot虚拟机中多数收集器都采用了分代收集来管理堆内存 那内存回收时就必须能决策哪些存 活对象应当放在新生代 哪些存活对象放在老年代中 为做到这点 虚拟机给每个对象定义了一个对象年龄 Age 计数器 存储在对象头中 对象通常在Eden
  • 视频插帧—学习笔记(算法+配置+云服务+Google-Colab)

    恰好碰到同学项目需要 了解了一下关于利用深度学习视频插帧的相关知识 在这里做一个简单的记录 目录 一 方法 论文 1 DAIN Depth Aware Video Frame Interpolation 2 FLAVR Flow Agnos
  • 自己动手实现简易STL

    自己动手实现简易STL STL六个组件 迭代器 算法 容器 迭代器部分 适配器 仿函数 额外的工作 小结 之前学习C 看侯老师的书的时候实现了一下STL的基本组件 包括了6个组件 allocator iterator container t