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

ifstreamofstream 是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的容器需要的全是小块内存,而且需求大小集中,因此自己设计一个自己用会效率更高,顺便解决内存碎片问题(解决了外碎片,但有内碎片)
相关推荐
z落落15 小时前
C# 泛型方法(原理、类型推断、多泛型参数)+泛型效率(普通类型 VS Object装箱 VS 泛型)
开发语言·c#
L_090715 小时前
【C++】异常
开发语言·c++
liulilittle15 小时前
关于拥塞控制的几点思考
网络·c++·tcp/ip·计算机网络·信息与通信·tcp·通信
世辰辰辰16 小时前
批量修改图片/文本名子
开发语言·python·批量修改文件名
QT-Neal17 小时前
C++ 编码规范
c++
啦啦啦啦啦zzzz17 小时前
数据结构:红黑树理论
数据结构·c++·红黑树
Yolo_TvT18 小时前
C++:默认构造函数
c++
z落落18 小时前
C# 四种特殊类:抽象类、密封类、静态类、部分类
开发语言·c#
VidDown18 小时前
Webhook 调试器:让第三方回调“原形毕露”
java·开发语言·javascript·编辑器·postman
装不满的克莱因瓶19 小时前
基于 OpenResty 扩展开发实现动态服务注册与发现能力
java·开发语言·架构·openresty