C++的IO流与STL的空间配置器

目录

C++的IO流

istream/ostream

ifstream/ofstream

istringstream/ostringstream

空间配置器

一级空间配置器

二级空间配置器

内存碎片问题

内碎片

外碎片


C++的IO流

在C++中,输入数据和输出数据用的是cin >> 和cout <<,cin的>>表示设备的数据流入对应变量,cout的<<表示对应变量数据流出到设备,因此这种输入输出的过程被形象的比喻为"流"。

istream/ostream

cin 是标准库中定义的istream类型的全局对象 ;cout 是标准库中定义的ostream类型的全局对象

iostream继承了istream和ostream,因此用iostream实例化的对象可以输入+输出

它们调用的>>、<<本质上是调用的operator>>() 与**operator<<()**方法

cpp 复制代码
int x;
cin >> x;
cin.operator>>(x);//与上面等价
cout << x;
cout.operator<<(x);//与上面等价

cout和cin不需要向C语言的scanf和printf一样手动指定类型,是因为cin的>>,cout的<<对每个内置类型都做了重载

cpp 复制代码
int x;
while(cin >> x)
{
    //
}

对于上面代码,可以保证有数据时一直循环,当输入Ctrl + Z (EOF标志)时再跳出循环,这本质是得益于istream类重载的operator bool 方法,允许流对象在布尔上下文中自动转换为bool值(istream的>>重载原本返回的是istream&类型)

ifstream/ofstream

ifstream 和ofstream 是C++中用于文件IO流的两个类,前者仅可以读取,后者仅可以写入 ,若想读取+写入可以用 fstream

cpp 复制代码
//模式默认是out,即以输出模式打开
ofstream ofs("text.log"/*,ios::out*/);//fopen("text.log","w");
ofs << "eee";//向文件中写入
ofs.close();//关闭文件
//模式默认是in,即以输入模式打开
string str;
ifstream ifs("text.log"/*,ios::in*/);//fopen("text.log",r);
ifs >> str;//读取到str中
cout << str;

若不在构造函数中指定文件,也可以后续用**open()**方法

cpp 复制代码
ofs.open("text.log"/*,ios::out*/);
ifs.open("text.log"/*,ios::in*/);

需要注意的是,ofstream的<<只会写入文本 ,例如ofs << 123,写入的也是1,2,3的ASCII码

而ofstream的write() 方法可以写入二进制码,例如ofs.write(123),写入的是4字节二进制值 :
0x7B 0x00 0x00 0x00(小端序)

ifstream的>>也只会读取文本 ,若写入时是以二进制写入,读取也需要以二进制读取,可以用 read() 方法,但要用read读取就必须在打开文件时指定模式ios::binary ,表示以二进制方式打开。

ifstream/ofstream的>>/<<的优点就是,++在想以字符串格式写入数字型数据时,不需要先把数字转换成字符串(例如tostring())++

cpp 复制代码
info student("张三",20);
ofstream ofs("text.log");
ofs << student._name << endl;
ofs << student._age << endl;//不需要转成字符串再传入
ofs.close();

info s;
ifstream ifs("text.log");
ifs >> s._name;
ifs >> s._age;

cout << s._name << ' ' << s._age << endl;

ps: fstream继承自iostream,而iostream又继承了ifstream和ofstream,因此fstream实例化的对象可读可写

istringstream/ostringstream

istringstream/ostringstream类用于将整型数据转换为字符串(例如itoa(),tostring()等)或将字符串转换为对应数据类型,前者可以从字符串中读取数据(输入流) ,常用于字符串解析和类型转换 ,后者用于向字符串写入数据 (输出流),常用于格式化拼接和类型转换

若要将不同类型的数据都转为字符串,就可以用ostringstream向字符串 写入数据

若要将字符串再分割为基本类型,就可以用istringstream

cpp 复制代码
//序列化(转换为字符串)
info student("张三",20);
ostringstream ost;
ost << student._name << endl;
ost << student._age << endl;

//反序列化(从字符串转换为数据)
info st;
istringstream ist;
ist.str(ost.str());
ist >> st._name >> st._age;

cout << st._name << ' ' << st._age << '\n';

ps: stringstream继承自iostream,而iostream又继承了istringstream和ostringstream,因此stringstream是支持读写的双向流,可解析可拼接

该对象常用于网络字符串拼接与解析

空间配置器

STL的空间配置器简单来说是内存池 ,负责在容器申请内存时分出内存,这避免了频繁申请内存导致的效率开销问题 。空间配置器本质也是以空间换时间 的策略,因为池化技术的缺点就是不用时也会占用资源。空间配置器分为一级空间配置器二级空间配置器

一级空间配置器

一级空间配置器就是malloc和free的封装 ,并处理失败抛异常机制。

在malloc开辟失败时,会先检测使用者有无设置失败的处理函数(一个函数指针),如果有就执行,否则抛异常。默认情况下没有设置该失败的函数指针句柄,即跟operator new基本一样,失败抛异常

要申请超过128字节 的内存时,才会使用一级空间配置器,若在128字节以内,会用二级空间配置器

二级空间配置器

二级空间配置器即为内存池

当容器申请内存时,内存池就分给容器对应字节的内存。但当容器用完内存时,不能单独释放这一小块内存 ,当时申请的多大内存就要释放多大内存,为了管理这些用完的内存,二级空间配置器加入了哈希桶(开散列)

索引以8字节为间隔 ,到128字节 ,当容器将内存还回来时,就会根据大小选择挂在哪个索引下面,当后续有容器想要申请内存时,就会先从哈希桶内查找有无匹配的内存

若容器申请的内存在哈希桶中没有,从内存池中申请时,也不会只切出对应字节的内存,而是直接++切出20个该对象内存,返回一个,剩下19个挂在哈希桶下面。++

由于容器申请的内存一般都为小块内存,二级空间配置器就是专门为了解决申请小块内存而出现的,因此这样做可以通过批量预分配显著减少系统调用频率,从而在频繁申请小块内存的场景下提升性能

一个进程中有一个空间配置器,进程中所有的容器需要内存,都会找空间配置器

内存碎片问题

内碎片

在二级空间配置器中,如果申请的不是8字节的整数倍,也会向上内存对齐 到8字节整数倍,而这样就会导致用于对齐的字节用不上,这就是内碎片问题

外碎片

当在堆上开辟了多个小块内存后,若后续其中的几块内存换回来,也有可能不连续

紫色×表示已释放,若此时再申请48字节内存,即使我们之前释放的空间也有48字节,但不是连续的 ,就不能用这两块空间申请,这就是外碎片问题,因此对于STL容器(常申请小块内存),就有了空间配置器来管理内存

内核中针对大量小块内存申请的碎片化问题,会使用slab分配器解决,它的结构类似于二级空间配置器

既然内核已经有slab分配器管理小块内存,为什么STL还需要二级空间配置器?

  • 内核是针对整个系统的所有程序的,并且每个都去堆申请,消耗特别大
  • STL的容器需要的全是小块内存,而且需求大小集中,因此自己设计一个自己用会效率更高,顺便解决内存碎片问题(解决了外碎片,但有内碎片)
相关推荐
郑州光合科技余经理1 分钟前
出海外卖平台:支付对接为什么要和下单收款分开
android·java·开发语言·前端·后端·php·ai编程
kimnoic8 分钟前
Python操作MySQL数据库的两种方式实例分析【pymysql和pandas】
开发语言·python
苏辰澈11 分钟前
C++独属于自己的变身:多态
c语言·开发语言·c++·visualstudio·编辑器
谢亮_vipxieliang12 分钟前
PHP 异常处理全解析:try-catch、自定义异常与全局异常处理器
开发语言·php
外收内放15 分钟前
Python基础语法练习题(65)
开发语言·python
水饺编程16 分钟前
第1章,[Win32 章节]:在 VS2019 里面安装 MSDN
c语言·c++·windows·visual studio
垆边人似月.17 分钟前
华为题:每日温度
数据结构·c++·算法
浅尝辄止;21 分钟前
设计模式在HIS/LIS医疗系统中的完整实践指南(C#版)
开发语言·设计模式·c#
Wang's Blog42 分钟前
Java 项目部署之 Docker工具快速入门: 容器命令练习:创建、改文件、重启到删除的完整闭环
java·开发语言·docker
j7~43 分钟前
【算法】专题四.前缀和:一维二维模板,配套 LeetCode 真题,结合同余定理、哈希前缀和,搞定子数组与子矩阵区间求和问题。
数据结构·c++·算法·前缀和·优选算法