【C++】IO流 + 空间配置器(了解)

文章目录

  • 📖 前言
  • 1. IO流
    • 1.1 C语言的输入和输出:
    • 1.2 流的概念及特性:
    • 1.3 自定义类型隐式类型转换:
    • 1.4 在线OJ中的输入和输出:
    • 1.5 C++IO流对文件的操作:
    • 1.6 stringstream介绍:
  • 2. 空间配置器
    • 2.1 什么是空间配置器:
    • 2.2 为什么需要空间配置器:
    • 2.3 STL空间配置器实现原理:
      • 2.3 - 1 内存池
      • 2.3 - 2 SGI-STL中二级空间配置器设计
      • 2.3 - 3 如何分配内存
    • 2.4 空间配置器在STL中的应用:
  • 3. 总结
  • 4. 赠言

📖 前言

本章讲简单的介绍C++的IO流和空间配置器,并学习一下C++STL库中空间配置器的底层结构,本章只是简单的了解学习,并不要求深入学习……🙌 🙌 🙌


1. IO流

1.1 C语言的输入和输出:

在我们刚学C语言的时候,我们就知道了输入和输出分别是scanfprintf

  • scanf: 从标准输入设备(键盘)读取数据,并将值存放在变量中。
  • printf: 将指定的文字/字符串输出到标准输出设备(屏幕),注意宽度输出和精度输出控制。

C语言是格式化的输入和输出:

  • scan是扫描的意思,我们可以理解成输入
  • print是打印的意思,我们可以理解成输出
  • f - 是format的缩写,是格式的意思

所以我们在用C语言输入和输出的时候,要指定格式,例如以整形的格式输入读取,以整形的格式输出,以字符串的形式输入输出……

C语言借助了相应的缓冲区来进行输入与输出,如下图所示:
在这里插入图片描述


1.2 流的概念及特性:

概念:

流: 即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据( 其单位可以是bit,byte,packet )的抽象描述。

C++的流:

  • C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。
  • 这种输入输出的过程被形象的比喻为 “流”。

流的特性:

  • 有序连续、具有方向性

为了实现这种流动,C++定义了I/O标准类库,这些每个类都称为流/流类,用以完成某方面的功能。

C++库中标准IO流:

在这里插入图片描述

  • C++系统实现了一个庞大的类库,其中ios为基类,其他类都是直接或间接派生自ios类。
  • 他们之间是继承的关系
  • 值得一提的是这里C++标准库中罕见使用了菱形继承。😀😀😀

既然有继承,那么这里就可以玩多态……

C++标准库提供了4个全局流对象cin、cout、cerr、clog,使用cout进行标准输出,即数据从内存流向控制台(显示器)。

int main()
{
	cout << "hello" << endl;
	cerr << "hello" << endl;
	clog << "hello" << endl;

	return 0;
}

这里结果都是hello。

补充:

使用cin进行标准输入即数据通过键盘输入到程序中,同时C++标准库还提供了cerr用来进行标准错误的输出,以及clog进行日志的输出,从上图可以看出,cout、cerr、clog是ostream类的三个不同的对象,因此这三个对象现在基本没有区别,只是应用场景不同,在使用时候必须要包含文件并引入std标准命名空间。

cin和cout可以直接输入和输出内置类型数据,原因:标准库已经将所有内置类型的输入和输出全部重载了。

istream重载:

在这里插入图片描述
ostream重载:

在这里插入图片描述


1.3 自定义类型隐式类型转换:

在我们之前的学习中,我们知道:括号()这个运算符,可以做函数调用的参数列表,也可以是控制优先级的,也可以是强制类型转换。

如果一个类要强转成其他类型,而我们之前学习的仿函数已经将括号()给重载了,所以我们要是想重载强制类型转换,就不能再用operator ()了,C++只能退而求其次搞了一种新的玩法。

见下述代码:

class Date
{
	friend ostream& operator << (ostream& out, const Date& d);
	friend istream& operator >> (istream& in, Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	//支持Date对象转换成bool
	//重载运算符,但是怎么重载类型了呢?
	//这里支持的就是将自定义类型转换成内置类型
	operator bool()
	{
		//这里是随意写的,假设输入_year为0,则结束
		if (_year < 1)
			return false;
		else
			return true;
	}

	//转换成整形
	operator int()
	{
		return _year + _month + _day;
	}

	//括号()这个运算符,可以做函数调用的参数列表,也可以是控制优先级的,也可以是强制类型转换
	//仿函数已经用了operator()了,那么强制类型转换就不能用operator()转换了

private:
	int _year;
	int _month;
	int _day;
};

在这里插入图片描述
这里支持的就是将自定义类型转换成内置类型。

注意:普通的operator后面接的都是运算符,而这里operator后面接的却是一个类型。

int main()
{
	Date d1 = -1;
	Date d2 = { 2023, 3, 23 };

	//自定义类型转换成内置类型
	bool ret1 = d1;
	bool ret2 = d2;

	//反过来转
	int i1 = d1;
	int i2 = d2;

	cout << ret1 << endl;
	cout << ret2 << endl;

	//支持转成bool,本质是调用了重载函数
	if (d1)
	{

	}

	return 0;
}

在这里插入图片描述
这里调用和运算符重载时一样,直接调用对应的operator 类型,这里就实现了自定义类型转成内置类型

就可以实现,内置类型的对象也能放在if或者while的判断中进行逻辑判断。


1.4 在线OJ中的输入和输出:

在我们之前的刷题中,一定遇到过多组输入的测试题目,C语言通常采用如下的方式:

在这里插入图片描述
我们知道scanf函数的返回值是一个整形,返回的是读取成功数据的个数,读取失败或者是读完之后会返回回个EOF

EOF:end of file的缩写,其值是-1,-1的补码全是1,取反则全是0,这就是第二种判断的原理

而C++中则是以下面的这种方式来写:

在这里插入图片描述
而我们之前在学习日期类中自己实现对日期类的输入和输出时,我们知道,ci对象重载之后返回的还是一个cin的对象。

istream& operator >> (istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

ostream& operator << (ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day;
	return out;
}

cin对象的内部肯定是支持重载成内置类型,所以可以将cin >> str,这种写法。


1.5 C++IO流对文件的操作:

两段程序仅供参考,这里了解性学习即可,毕竟C语言中我们详细学了文件的操作,到现在也没怎么用过嘛~😁😁😁

int main()
{
	//是读文件
	//in是读取,out才是写文件
	ifstream ifs("text.cpp");

	//重载了operator bool
	//正常读取返回true,读取失败或者读到文件结尾了就返回false
	//while (ifs)
	//{
	//	//一个字符一个字符的读,读出来之后打印
	//	char ch = ifs.get();
	//	cout << ch;
	//}

	//自动把空格忽略掉了
	char ch;
	while (ifs >> ch)
	{
		cout << ch;
	}

	return 0;
}

第一个循环可以直接将文件原封不动的读完,第二个循环则是会自动忽略掉空格~

从一个文件读到另一个文件当中:

int main()
{
	ifstream ifs("text.cpp");
	ofstream ofs("Copy.cpp");

	char ch;
	ch = ifs.get();
	//while (ifs)
	while (~ch)
	{
		//插入到文件当中去
		ofs << ch;
		cout << ch;

		ch = ifs.get();
	}

	return 0;
}

1.6 stringstream介绍:

序列化和反序列化:

  • 序列化就是将数据转化成字符串,反序列化就是将字符串转化成对应的数据类型

在C语言中,我们若是想要将一个整型变量的数据转化为字符串格式,有以下方法:

  • 使用sprint函数进行转化
int main()
{
	int a = 10;
	char arr[10];
	sprintf(arr, "%d", a); 

	cout << arr << endl;

	return 0;
}

将整型的a转化为字符串格式存储在字符串arr当中。

C++中的stringstream:

在这里插入图片描述

int main()
{
	//把整形转成字符串,相当于将整形插入进去就转成字符串了
	int i = 123;
	double d = 44.55;
	ostringstream oss;
	oss << i;

	cout << oss.str() << endl;
	string stri = oss.str();

	//赋值覆盖一下
	oss.str("");
	oss << d;
	string strd = oss.str();

	oss.str("");
	Date d1(2022, 10, 11);
	oss << d1;
	string strdt = oss.str();

	cout << strdt << endl;

	istringstream iss(strdt);
	Date d2;
	iss >> d2;


	return 0;
}

stringstream 用来转换自定义类型还是有优势,可以很方便的将整形转字符串,字符串转整形。

综上总结:

  • istream 和 ostream 底层本质是在做类型转换,转成字符串存起来
  • 然后存储的字符串的去向不同

2. 空间配置器

2.1 什么是空间配置器:

空间配置器,顾名思义就是为各个容器高效的管理空间(空间的申请与回收)的,在默默地工作。


2.2 为什么需要空间配置器:

前面在模拟实现vector、list、map、unordered_map等容器时,所有需要空间的地方都是通过new申请的,虽然代码可以正常运行,但是有以下不足之处:

  1. 空间申请与释放需要用户自己管理,容易造成内存泄漏
  2. 频繁向系统申请小块内存块,容易造成内存碎片
  3. 频繁向系统申请小块内存,影响程序运行效率
  4. 直接使用malloc与new进行申请,每块空间前有额外空间浪费
  5. 申请空间失败怎么应对
  6. 代码结构比较混乱,代码复用率不高
  7. 未考虑线程安全问题

所以我们需要设计一块高效的内存管理机制。


2.3 STL空间配置器实现原理:

  • 我们这里学习的是SGI版本的STL空间配置器的结构:
  • SGI-STL128作为小块内存与大块内存的分界线
  • 将空间配置器其分为两级结构
  • 一级空间配置器处理大块内存,二级空间配置器处理小块内存

2.3 - 1 内存池

内存池就是: 先申请一块比较大的内存块已做备用,当需要内存时,直接到内存池中去去,当池中空间不够时,再向内存中去取,当用户不用时,直接还回内存池即可。避免了频繁向系统申请小块内存所造成的效率低、内存碎片以及额外浪费的问题。
在这里插入图片描述
首先我们先储备知识,我们需要空间需要向堆申请,malloc也是个内存池,它提供服务的对象是整个程序,内空间配置器则是针对STL容器服务的。同时不同版本的malloc实现的方式也是不一样的,malloc每次开空间大小也并不是要的大小,会多一点,比如空间前面会记录这块空间多大,留释放用。

关系图:
在这里插入图片描述
空间配置器就是单独开一块大的空间,留给申请小空间的用,就不用频繁的到堆上取,造成内存碎片化。

2.3 - 2 SGI-STL中二级空间配置器设计

在这里插入图片描述
当申请的空间大于128 Byte的时候就不会走内存池,而是直接用malloc去直接申请,当申请的内存小于128 Byte的时候则会走内存池申请。

  • SGI-STL中的二级空间配置器使用了内存池技术
    • 但没有采用链表的方式对用户已经归还的空间进行管理
    • 因为用户申请空间时在查找合适的小块内存时效率比较低
    • 而是采用了哈希桶的方式进行管理。
  • 那是否需要128桶个空间来管理用户已经归还的内存块呢?
    • 答案是不需要,因为用户申请的空间基本都是4的整数倍,其他大小的空间几乎很少用到。
    • 因此:SGI-STL将用户申请的内存块向上对齐到了8的整数倍
  • 内存还回来还要继续给别人用,哪块内存符合再来申请空间的大小呢?要挨个挨个遍历,效率低了,为了方便管理,运用到了哈希桶。

示意图:
在这里插入图片描述
挨个挨个用链表挂起来,那待会再来申请内存的时候优先再这取,每个桶中挂的是内存块,取内存的时候直接在对应的桶中取就好。

  • 128个桶的话,分的太细不好,内存太碎了,所以在这里给了八字节对齐这种玩法。
  • 0号桶挂的是8Byte,1号桶挂的是16Byte…
  • 内存大小 == (访问的桶 + 1)* 8

2.3 - 3 如何分配内存

如果申请的空间大于128Byte的话,直接走一级空间配置器,调用malloc函数,如果是小于128Byte的话,则是走二级空间配置器。

  • 先到哈希桶找,再去内存池找,再去malloc找(此时才申请内存池),所有申请内存都先来找哈希桶。

每次给的内存的大小:

  • 每次申请的内存块大小不一定是桶中正好有的内存块的大小
  • 内存块挂起来管理,按一定规则对齐, 就会导致内碎片
    • 此时我们就会多给一点,给比申请的内存稍微大一点的内存
    • 例如,我们需要7个字节,就会给8个字节,需要12个字节,就会给16个字节,需要14个字节,也会给16个字节。
  • 空间配置器,只会多给,不会克扣
  • 这里最多浪费7字节,当然桶越多浪费就越少,因为内存被分的越碎
  • 如果桶为空,则向内存池中申请一大块内存,然后第一个留下有用,剩下的分为同样的所需大小,挂在桶中。
  • 下次再来找相同的大小快的内存,就不用再去找内存池了,而是直接在对应的桶中头删一个。
  • 如果内存池用完了就再去malloc,只要系统有内存就可以申请。
  • 内存还回来以后,可以再次用而且申请空间的效率非常的高小于128Byte直接到桶里取内存就可以了。
  • 如果对应的哈希桶没有内存了,内存池也没了,堆上的空间也用完了,它就会到其他的桶中去看有无内存,例如:会将60Byte的前40Byte切出来留用,后20Byte挂起来。

优点:

  • 用哈希桶的好处是,要多大的内存可以很快的找到对应的链表,要是释放也能很快的找到对应的链表。
  • 时间复杂度:申请内存是O(1),释放内存也是O(1),因为是哈希桶的头删和头插。
  • 内存还回来以后可以再次用而且申请空间的效率非常的高,小于128Bvte直接到桶里取内存就可以了。
  • 内存块两用的,给分配的用,或者记录下个内存块地址,链接用把自身管理起来。
  • 在头四个或者是八个Byte存下一个内存的地址就可以了最小的内存块是4字节,不存在存不下下一个内存块地址的问题,因为无论在哪个平台下,指针的大小要么是4Byte,要么是8个Byte

补充:

一个进程里面只有一 个空间配置器也就是说空间配置器可以设置成单例模式库里的实现和单例一样都能保证只有一对象。


2.4 空间配置器在STL中的应用:

我们以list为例:在这里插入图片描述
在这里插入图片描述


3. 总结

本章内容只需了解,要知道其底层大概的结构,明白其思想即可。


4. 赠言

至此,C++和数据结构的学习暂时告一段落,感谢一路相伴,我们Linux再见~~😊😊😊👋👋👋


少年所要走的路才不会有孤独,因为每一步都有属于自己的使命与归途。往事暗沉不可追,来日之路光明灿烂。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安科瑞智能照明控制系统在工厂的应用

安科瑞 安科瑞 李亚娜 1&#xff5c;概述 安科瑞智能照明控制解决方案ALIBUS&#xff08;Acrel Lighting intelligent Bus&#xff09;基于成熟的RS485通讯控制技术&#xff0c;同时创新地引入了载波侦听和冲突碰撞检测机制&#xff0c;多机间实现了实时双向通讯&#xff0c;线…

Java设计模式-9 、策略模式

策略模式 策略模式&#xff08;Strategy Pattern&#xff09;属于对象的⾏为模式。其⽤意是针对⼀组算 法&#xff0c;将每⼀个算法封装到具有共同接⼝的独⽴的类中&#xff0c;从⽽使得它们可以 相互替换。策略模式使得算法可以在不影响到客户端的情况下发⽣变化。 其主要⽬的…

基于Python长时间序列遥感数据处理及在全球变化、物候提取、植被变绿与固碳分析、生物量估算与趋势分析等领域中的应用

植被是陆地生态系统中最重要的组分之一&#xff0c;也是对气候变化最敏感的组分&#xff0c;其在全球变化过程中起着重要作用&#xff0c;能够指示自然环境中的大气、水、土壤等成分的变化&#xff0c;其年际和季节性变化可以作为地球气候变化的重要指标。此外&#xff0c;由于…

手敲Mybatis(八)-参数处理器

前言在之前的章节里边&#xff0c;类PreparedStatementHandler我们还没有处理在执行Sql时的参数&#xff0c;目前是硬编码写死存储的&#xff0c;如&#xff1a;ps.setLong()&#xff0c;这里就只能处理long类型的数据因为写死了&#xff0c;我们需要处理下让它支持设置不同的数…

【Linux 网络编程4】网络层--UDP/TCP协议,3次握手4次挥手、粘包问题等

netstat命令-n.拒绝显示别名&#xff0c;能显示数字的全部转化成数字(IPPORT)-l 仅列出有在 Listen (监听) 的服务的状态-p 显示建立相关链接的程序名&#xff08;pid&#xff09;-t 仅显示tcp相关选项-u 仅显示udp相关选项 2.UDP协议2.1.全双工和半双工的区别全双工&#xff1…

了解Session、LocatStorage、Cache-Control、ETag区别

一、cookie与session有什么区别&#xff1f; 1. 由于HTTP协议是无状态的协议&#xff0c;所以服务端需要记录用户的状态时&#xff0c;就需要用某种机制来识具体的用户&#xff0c;这个机制就是Session.典型的场景比如购物车&#xff0c;当你点击下单按钮时&#xff0c;由于HT…

SpringBoot学习笔记(4)-分析 SpringBoot 底层机制

文章目录4. 分析 SpringBoot 底层机制4.1 Tomcat启动分析4.2 创建Spring 容器4.3 将Tomcat 和 Spring 容器关联&#xff0c;并启动 Spring 容器4.4 扩展-debug查看 ac.refresh()4. 分析 SpringBoot 底层机制 【Tomcat 启动分析 Spring 容器初始化Tomcat 如何关联 Spring 容器…

微软分享修复WinRE BitLocker绕过漏洞的脚本

微软发布了一个脚本&#xff0c;可以更轻松地修补 Windows 恢复环境 (WinRE) 中的 BitLocker 绕过安全漏洞。 此 PowerShell 脚本 (KB5025175) 简化了保护 WinRE 映像以防止试图利用CVE-2022-41099漏洞的过程&#xff0c;该漏洞使攻击者能够绕过 BitLocker 设备加密功能系统存…

jvm03垃圾回收篇

p134 垃圾回收相关章节的说明 p135 什么是GC 为什么需要GC P136 了解早起垃圾回收行为 p137 java自动内存管理介绍 p138垃圾回收相关算法概述 p139引用计数算法的原理及优缺点 p140 python引用计数实施方案 p141 可达性分析算法与GC ROOTS p142 对象的finalization机制 p143 代…

【MyBatis】字段名和属性名不同时,如何处理

目录 前言 1、返回类型&#xff1a;resultType 2、返回字典映射&#xff1a;resultMap 2.1、字段名和属性名不同怎么处理 解决方案一&#xff1a;使用resultMap 解决方案二&#xff1a;使用as起别名 3、多表查询 总结&#xff1a; 前言 在之前的文章中&#xff0c;我们可…

TXT 和 SEV技术小知识

1.Intel TXT 可信执行技术(Trusted Execute Technology&#xff0c;TXT)是Intel公司的可信计算技术&#xff0c;主要通过改造芯片组和CPU&#xff0c;增加安全特性&#xff0c;通过结合一个基于硬件的安全设备—可信平台模块(Trusted Platform Module&#xff0c;TPM)&#xf…

蓝桥杯C/C++VIP试题每日一练之Sine之舞

💛作者主页:静Yu 🧡简介:CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家,前端知识交流社区创建者 💛社区地址:前端知识交流社区 🧡博主的个人博客:静Yu的个人博客 🧡博主的个人笔记本:前端面试题 个人笔记本只记录前端领域的面试题目,项目总结,面试技…

Python 之列表推导式

文章目录参考描述列表推导式举个栗子基本形式一般式基本形式&#xff08;高阶&#xff09;判断使用逻辑运算符笛卡尔积拆解变量污染列表推导式参考 项目描述流畅的 PythonLuciano Ramalho 著 / 安道 吴珂 译搜索引擎Bing 描述 项目描述Python3.10.6 列表推导式 列表推导式是…

python---函数的进阶

函数的进阶 1.8函数的进阶 1.8.1函数作为参数进行传入 1.简介&#xff1a;函数作为范围进行传递到函数中进行操作 2.函数作为参数传入到函数中 3.函数调用和逻辑传入之间的区别 一个是作为数据进行传入&#xff0c;但是调用的函数时一定的一个作为逻辑进行调用&#xff0c;但是…

PTA——1036 跟奥巴马一起编程、1037 在霍格沃茨找零钱、1038 统计同成绩学生、1039 到底买不买、1040 有几个PAT

1036 跟奥巴马一起编程 解决代码 模拟题目即可。 #include<iostream> #include<vector> #include<algorithm> using namespace std; int main(){int n;char c;cin>>n>>c;double col(double)n/2;int n0n/2;if(col>n00.4) n01; for(int i0;i&…

华为路由器 基本ACL配置

1、什么是ACL&#xff1f; 访问控制列表ACL&#xff08;Access Control List&#xff09;是由一条或多条规则组成的集合。所谓规则&#xff0c;是指描述报文匹配条件的判断语句&#xff0c;这些条件可以是报文的源地址、目的地址、端口号等。 ACL本质上是一种报文过滤器&…

Android 高通Camera2 Camera Device Close

1、很多人看到这个日志第一感觉可能觉得哪里没有合理释放&#xff0c;于是带着这个思路去进行百度探索 2、一开始我去寻找 ImageReader.OnImageAvailableListener 这个问题 var afterBitmap: Bitmap? null/**监听拍照的图片 */private val imageAvailableListener ImageRead…

GPT免费网站分享(持续更新)

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

从二叉查找树到B*树,一文搞懂搜索树的演进!|金三银四系列

对于准备面试这篇再适合不过了&#xff01;详细讲解了从BST、AVL、红黑树、B树、B树最后到B*树的演进过程&#xff0c;以及各种结构的优劣。点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;优质资源及时送达在面试中&#xff0c;面试官很容易抛出这样的问题…

Java设计模式-2、⼯⼚模式

⼯⼚模式 工厂模式是对简单工厂的一个衍生&#xff0c;解决了许多简单工厂模式的问题。 一、说⼀说简单⼯⼚模式 简单⼯⼚模式指由⼀个⼯⼚对象来创建实例&#xff0c;客户端不需要关注创建逻 辑&#xff0c;只需提供传⼊⼯⼚的参数。 适⽤于⼯⼚类负责创建对象较少的情况&a…
最新文章