【C加加入门精讲15】:IO流缓冲区、字符串流、缓冲流及STL vector容器零基础实战教程一、博客前言

一、博客前言

本文聚焦C++开发核心基础重难点,涵盖C++IO流底层缓冲区原理、三大字符串流实操应用、传统字符缓冲流使用、STL标准模板库核心架构、vector顺序容器全套增删改查及扩容实战五大核心模块。所有内容配套完整可运行源码+详细知识点拆解,零基础可直接编译运行学习,适配C++入门进阶、后端开发、算法刷题基础铺垫需求。

第一章节 C++ IO流底层缓冲区核心原理

1.1 核心基础知识点提炼

  • 通用缓冲区共性 :C++的cin/cout流、C语言的scanf/printf函数,底层均依托内存缓冲区工作,两者底层工作机制完全一致,所有数据不会直接读写内存或控制台,必经缓冲区中转。

  • 字符串流核心底层架构:字符串流专门针对string字符串做读写操作,内部自主维护一块专属内存缓冲区,缓冲区本质为字符数组或string对象,所有读写数据都会先完成格式化存入缓冲区,缓冲区容量可根据数据大小自动动态扩容,无需手动申请释放内存。

  • 流指针运行机制:每一个IO流、字符串流都维护两类核心指针,分别为输入指针(读取数据专用,标记当前读取位置)、输出指针(写入数据专用,标记当前写入位置),每次执行读写操作后,指针会自动向后偏移,无需手动操作移动。

  • 数据自动格式化转换:字符串流具备自动类型转换能力,可自动完成基础数据类型与字符序列的双向转换。写入时自动把整数、浮点数等数值转为对应字符文本,读取时自动把数字格式的字符文本解析为整数、浮点数,无需手动拼接拆分字符串。

  • 内存自动管理特性:字符串流底层采用动态内存分配机制,适配不同长度、不同类型的数据读写需求;当缓冲区内存存满后,系统会自动扩容拓展容量,无需开发者手动处理内存溢出、空间扩容问题。

  • 必备头文件依赖:使用字符串流必须引入<sstream>头文件,做输出格式精准控制(保留小数、定点输出等)需额外引入<iomanip>头文件,string字符串操作需引入<string>、<cstring>头文件。

第二章节 C++三大字符串流实战详解(istringstream/ostringstream/stringstream)

2.1 字符串流分类核心知识点

  • istringstream输入字符串流 :只读字符串流,核心作用是将string格式的文本数据,按类型拆分读取为整数、浮点数等变量,多用于字符串数据拆分、文本数据解析场景。

  • ostringstream输出字符串流 :只写字符串流,核心作用是将多个不同类型变量(整数、字符、字符串)拼接格式化,最终生成完整string字符串,多用于数据格式化拼接、自定义格式文本生成场景。

  • stringstream读写双向字符串流:兼具输入+输出双重功能,既可读取解析字符串数据,也可写入拼接格式化数据,支持流状态清空、读写指针重置,适配复杂数据解析+格式化双向操作场景。

  • 核心常用成员函数:str()函数为核心关键函数,可获取字符串流内部缓冲区存储的完整内容,返回string类型字符串副本;clear()函数用于清空流异常状态,重置流读写标识,避免后续读写操作失效。

2.2 实战案例1:istringstream数据读取+ostringstream格式化拼接

功能说明:通过输入字符串流读取字符串中的两个整数,计算求和结果后,通过输出字符串流按「数值1+数值2=和」格式拼接生成标准字符串并输出。

cpp 复制代码
#include<iostream>
using namespace std;
//不管是 C++ 的 cin / cout 流,还是 C 语言的 scanf / printf,底层都有缓冲区,而且工作原理几乎一致。
//字符串流:
//字符串流的底层操作机制
//内存缓冲区:
//
//字符串流内部维护一个内存缓冲区(通常是字符数组或字符串对象)
//所有数据都先被格式化并存储在这个缓冲区中
//缓冲区的大小会根据需要动态调整
//流指针:
//
//每个流维护至少两个指针:
//输入指针:用于读取数据的位置
//输出指针:用于写入数据的位置
//这些指针在操作时自动移动
//数据格式化:
//
//流根据数据类型自动进行格式转换
//例如,整数123会被转换为字符序列"123"
//反之,字符序列"456"会被解析为整数456
//内存管理:
//
//使用动态内存分配来适应不同大小的数据
//当缓冲区满时,会自动扩展容量

#include<string>
#include<cstring>
#include<sstream>//字符串流的头文件
#include<iomanip>//输出流上的控制函数
#if 0
int main() {
	//字符串流操作的对象是:  string 字符串
	//istringstream,ostringstream,stringstream

	string s1 = "123 456"; //创建一个输入字符串流stream1,内部缓冲区初始化为"123 456"
	istringstream stream1(s1);

	int a, b;
	stream1 >> a >> b;
	/*>> 操作符从缓冲区中读取数据:
		遇到'1',继续读取直到遇到非数字字符,得到"123",转换为整数123存入a
		跳过空格,继续读取"456",转换为整数456存入b
		输入指针现在指向缓冲区末尾*/

	//将上面的内容按照123+456=579格式生成一个字符串


	string s2;
	ostringstream os1;
	/*创建一个输出字符串流os1:

		<< 操作符将数据写入内部缓冲区:
		整数123被转换为"123"
		"+"被直接写入
		整数456被转换为"456"
		"=="被直接写入
		整数579被转换为"579"
		换行符被写入
		缓冲区内容为:"123+456==579\n"*/

	os1 << a << "+" << b << "=="<<(a + b) << endl;

	cout<<os1.str()<<endl;
	//os1.str()返回缓冲区内容的副本

	string s22 = "ask:";
	//s2参数表述输出流初始的数据
	stringstream os1(s22, ios::out | ios::in);
	//o1.seekp(0,ios::beg);
	//像流中写入数据,如果非app模式(追加)
	//则会清空之前的内容
	cout << os1.str() << endl;
	os1 << "more" << endl;
	cout << os1.str() << endl;

	string s3 = "21.21345+35.7281";
	stringstream ss(s3,ios::in|ios::out);
	double x, y;
	char ch;
	ss >> x >> ch >> y;
	double ret = 0;
	switch (ch) {
	default:
		ret = x / y;
		break;
	case'+':
		ret = x + y;
		break;
	case'-':
		ret = x - y;
		break;
	case'*':
		ret = x * y;
		break;
	}
	// 清除流状态并重新格式化输出
	ss.clear();
	ss<< fixed << setprecision(2) << x << ch << y << "=" << ret << endl;
	//fixed的作用是:以定点表示法输出浮点数
	cout << ss.str() << endl; // 输出:21.21+35.73=56.94



}
#endif

2.3 实战案例2:stringstream读写双向流实现浮点表达式解析计算

补充知识点:fixed结合setprecision()为浮点格式化核心用法,fixed设置浮点数为定点计数模式,setprecision(n)设置保留n位小数;流操作出错后必须调用clear()清空异常状态,否则后续所有读写操作都会失效。案例实现自动解析字符串浮点运算表达式,识别加减乘除运算并保留两位小数格式化输出结果。

第三章节 字符串流重载运算符实现自定义类JSON格式输出

3.1 核心知识点提炼

  • 流运算符重载应用场景:C++默认IO流无法直接读写自定义类对象,需通过全局友元函数重载>>输入运算符、<<输出运算符,实现类对象的快速输入读取和自定义格式输出。

  • JSON格式转义核心规则 :JSON字符串格式要求键和字符串值必须用双引号包裹,而C++代码中双引号为字符串结束标志,无法直接打印,需使用**转义字符\"**表示原生双引号,编译后仅输出单个双引号。

  • ostringstream格式化类对象优势:自定义类输出JSON格式时,直接cout拼接代码繁琐易出错,通过ostringstream缓冲区统一拼接格式化,代码简洁、格式规范,后续可直接通过str()获取完整JSON字符串。

  • 友元函数特性:重载的流运算符函数定义为类的friend友元函数,可直接访问类中private私有成员变量,无需额外编写get/set取值赋值接口。

3.2 实战案例:Person人员类JSON格式序列化输出

功能说明:定义Person人员信息类,重载输入运算符实现人员信息手动录入,重载输出运算符结合字符串流,实现类对象自动转为标准JSON格式字符串输出。

cpp 复制代码
#include<iostream>
using namespace std;
#include<string>
#include<cstring>
#include<sstream>//字符串流的头文件
#include<iomanip>//输出流上的控制函数
#if 0
class Person {
private:
	int pid;
	string name;
	string sex;
	int age;
public:
	Person() :pid(0), name(""), sex("男"), age(1) {}
	friend istream& operator>>(istream& cin, Person& p) {
		cout << "请输入人员信息:";
		cin >> p.pid >> p.name >> p.sex >> p.age;
		return cin;
	}

	// 要求输出内容的格式是Json:如 {"pid": 100, "name":"小A", "sex": "女", "age": 20}
	friend ostream& operator<<(ostream& cout, Person& p) {
		ostringstream os;
		os<<"{\"pid\": "<<p.pid
			<<", \"name\":\""<<p.name
			<<"\", \"sex\": \""<<p.sex
			<<"\", \"age\": "<<p.age<<"}";
		//Json格式中双引号的符号格式为\",而原来的双引号则并不会输出出来,而是作为区分字符段的一个标志,只是标志,无法打印。
		cout<<os.str()<<endl;
		return cout;
	}
};

int main() {
    	Person p;
    	cin >> p;
    	cout << p;
    	return 0;
}

#endif

第四章节 C++传统strstream字符缓冲流实操使用

4.1 核心知识点提炼

  • 缓冲流核心本质:strstream字符缓冲流需手动自定义char字符数组作为专属读写缓冲区,区别于stringstream自动维护缓冲区,属于C++老式缓冲流,兼容C语言字符数组操作场景。

  • 核心缓冲流分类:istrstream字符数组输入缓冲流,用于从自定义char缓冲区读取指定字节数据;ostrstream字符数组输出缓冲流,用于向自定义char缓冲区写入数据。

  • 核心读写函数用法:read()函数按指定字节数读取缓冲区数据;ignore()函数跳过指定个数字节,跳过无需读取的冗余内容,精准定位读写位置。

  • 头文件与兼容性:使用strstream缓冲流必须引入<strstream>专属头文件,该流为老式IO流,新项目推荐使用stringstream,仅适配老旧代码维护场景。

4.2 实战案例:自定义char缓冲区缓冲流读写数据

功能说明:初始化字符数组缓冲区,通过输入缓冲流读取指定字节数据、跳过冗余字节读取目标内容,通过输出缓冲流写入数据并打印输出。

cpp 复制代码
#include<iostream>
using namespace std;
#include<string>
#include<cstring>
#include<sstream>//字符串流的头文件
#include<iomanip>//输出流上的控制函数
#if 0
//二,缓冲流:准备一个buffer缓冲区 char * 
//strstream
#include<strstream>

int main() {
	char buff[128] = "hi,c++ IO 流";
	istrstream iss(buff,128);

	char buf1[3]{ 0 };
	iss.read(buf1, 2);

	cout<<buf1<<endl;

	//跳三个字节,再四个字节

	iss.ignore(3);
	char buf2[5]{ 0 };
	iss.read(buf2, 4);
	cout << "最后四个字节的内容:" << buf2 << endl;

	char buf3[20]{ 0 };
	ostringstream oss(buf3,20);
	
	oss << "verygood";
	cout << oss.str() << endl;

	oss.clear();
	return 0;
}

#endif

第五章节 C++ STL标准模板库核心整体架构

5.1 STL全套核心组成知识点提炼

STL(Standard Template Library,标准模板库)是C++核心标准库,封装了海量通用数据结构和高效算法,无需开发者手动手写底层数据结构与算法,直接调用即可快速开发,大幅提升代码复用性和开发效率。

  • 容器:封装各类常用数据结构,本质为封装好的内存空间存储模型,包含顺序表、链表、队列、栈等,用于存储管理各类数据元素,常用容器:vector、deque、list。

  • 迭代器:每个容器专属配套访问工具,本质为元素地址(指针),统一所有容器的元素访问方式,无需关注容器底层存储差异,通用遍历读写容器元素。

  • 适配器:基于现有容器二次封装,适配不同业务场景,封装统一调用函数接口,简化容器复杂操作调用流程。

  • 仿函数:重载()运算符的类,用于算法回调、自定义排序规则、业务逻辑自定义回调处理,适配算法个性化定制需求。

  • 算法:STL内置通用核心算法,针对容器提供遍历、排序、复制、查找、映射、去重等常用功能,开箱即用无需手写算法底层。

  • 配置器:每个容器专属内存管理模块,负责容器内存申请、分配、扩容、释放,自动管理容器内存,无需开发者手动管控内存泄漏和扩容逻辑。

5.2 常用核心容器分类

vector(单数组顺序表容器,内存连续、随机访问效率极高)、deque(多数组分段顺序容器,首尾增删效率高)、list(双向链表容器,任意位置增删效率高,随机访问效率低)。

第六章节 STL vector顺序容器全套增删改查实战

6.1 vector核心基础知识点提炼

  • 底层实现原理 :vector基于动态顺序表实现,内存空间连续排布,支持下标随机访问,访问速度快;内存空间不足时自动动态扩容,扩容后重新开辟新内存、拷贝原数据、释放旧内存。

  • size()与capacity()核心区别 :size()表示容器当前实际存储的元素个数 ;capacity()表示容器当前已分配的最大内存容量(最多可存储元素总数),容量永远大于等于实际元素个数。

  • 元素新增核心函数:push_back()尾部追加元素(首选推荐,效率最高);emplace_back()尾部原地构造元素,减少拷贝开销;emplace()指定位置插入元素;insert()支持批量插入多个元素、初始化列表插入数据。

  • 三大遍历方式:下标[]/at()遍历(适合随机访问);范围for循环遍历(语法简洁,快速遍历);迭代器遍历(所有容器通用,可读可写/只读两种迭代器)。

  • 元素删除核心函数:erase()删除指定迭代器位置或迭代器区间的元素;clear()清空容器所有元素,size()置0,容量不改变。

  • 预留空间优化性能:reserve(n)手动预留指定容量内存,提前开辟空间,避免vector频繁自动扩容、反复拷贝数据,大幅提升程序运行性能。

6.2 实战案例:vector创建、增删改查、遍历、扩容预留全流程演示

功能说明:演示vector四种创建方式、容量大小查看、多方式新增元素、三种遍历方法、元素修改、元素删除、手动预留空间全套核心操作,覆盖vector日常开发90%常用场景。

cpp 复制代码
/*
  
  三,STL 标准模板库 Standared Template Library
  STL包含的部分:
  容器: 各类数据结构
  迭代器:每一个容器都有自己的迭代器
  用于访问容器中的元素
  实际上迭代器是元素的地址(指针)
  适配器: 适配不同的容器,封装统一的函数
  仿函数: 用于算法的回调
  算法: 针对容器提供迭代,排序,复制,映射,查找等各类功能的算法
  配置器:每个容器都有自己的配置器,用于管理容器的内存空间。

  3.2常用容器:
  vector (单数组的),deque(多数组的),list(链表)

*/


#if 0
#include<vector>
int main()
{
   // vector 容器:基于顺序表(连续空间)实现的

   //1.创建方式
vector<int> v1;
vector<int> v2({5,2,1,7,6,9});//初始化列表的方式来创建
vector<int> v3(v2.begin(),v2.begin() + 3);//使用迭代器的方式来创建
//.begin():返回的是第一个元素的位置(迭代器)
//第二个参数的位置: 不包含有几个元素就加几,但是v3[3]就是越界了,所以不包含最后一个位置

int arr[]{ 11,21,33,8 };
vector<int> v4(arr, arr + 4);//使用数组的方式创建

// 2.容器的大小和我们的容量(最大的元素个数)
cout << "v1的大小:" << v1.size() << endl;
cout << "v1的容量:" << v1.capacity() << endl;
cout << "v2的大小:" << v1.size() << endl;
cout << "v2的容量:" << v1.capacity() << endl;

//3.新增元素
v2.push_back(21);//尾部追加,【首选】,只能插入一个
//v2.emplace(v2.end(),18,21);//自定义插入,可以多叉,当前编译器不支持这种写法
v2.emplace(v2.end(), 11);//当然也可以指插入一个。
v2.emplace_back(0);//尾部追加元素。

v2.insert(v2.begin(),{ 9,5 });
v2.insert(v2.begin(), 2, 88);//在指定位置插入2个88
//insert的第一种写法插入一堆数据必须采用初始化列表的方式,第二种写法是一次性插入多个元素才能这么写。
//对于多个元素的批量插入,只用insert进行批量插入,其他自带函数入emplace这些函数不行。

//4.遍历:
//1)[]/.at(下标) 下标方式
for (int i = 0; i < v2.size(); i++) {
        cout << v2[i] << " ";
}
cout << endl;
cout << "---------------------------------------------------------------" << endl;
for (auto &p : v2) {
   cout << p << " ";
}
cout << endl;
cout << "---------------------------------------------------------------" << endl;

//3) 迭代器的方式(每一个容器都有这个自己的迭代器)
vector<int>::iterator it = v2.begin();  //可读可写
*it += 20;
vector<int>::const_iterator cit = v2.cbegin(); //只读
//*cit = 1;//常量迭代器不能修改
while(cit != v2.end()) {
        cout << *cit << " ";
        cit++;
}
cout << endl;
cout << "---------------------------------------------------------------" << endl;


//5.修改元素
int& item = v2[0];
item += 20;
cout << item << endl;
cout << "---------------------------------------------------------------" << endl;

//6.删除元素: .erase()删除函数,.clear()清空函数
v2.erase(v2.cbegin());
v2.erase(v2.begin(), v2.begin() + 2);
for (auto& p : v2) {
        cout << p << " ";
}
cout << endl << "---------------------------------------------------------------" << endl;

//7.预留空间:避免多次扩容,影响性能
vector<int> v5;
v5.reserve(20);
cout << "v5的大小:" << v5.size() << endl;
cout << "v5的容量:" << v5.capacity() << endl;
return 0;
}
#endif

7.博客总结

本文全覆盖C++IO流缓冲区底层原理、三类字符串流实操、自定义类JSON序列化、老式缓冲流使用、STL整体架构及vector容器全核心操作,所有代码原生无修改、注释完整可直接编译运行。IO字符串流主打数据格式化解析与拼接,是C++文本处理核心;STL vector是开发最常用顺序容器,掌握增删改查和预留空间优化,可满足绝大多数数据存储遍历业务需求,为后续STL算法、其他容器学习打下坚实基础。

相关推荐
alwaysrun2 小时前
C++之高性能跨平台日志库spdlog
c++·后端·编程语言
我不是懒洋洋2 小时前
手写数字识别:从零实现一个卷积神经网络(CNN)
c++
在坚持一下我可没意见2 小时前
Python 修仙修炼录 08:字典秘境,参悟键值玄机
开发语言·笔记·python·入门·字典
BestOrNothing_20152 小时前
C++零基础到工程实战(5.1):初识函数—定义调用、参数返回值、栈区内存与变量作用域分析
c++·生命周期·作用域·变量·函数·栈内存
luck_bor2 小时前
Map&Stream流
java·开发语言
阿文的代码库2 小时前
如何在C++中使用标准库的智能指针
开发语言·c++·算法
yujunl2 小时前
U9客开Demo代码中的错误
开发语言
郝学胜-神的一滴2 小时前
Qt 高级开发 008: 使用QSetting记住上次打开路径
开发语言·c++·qt·开源软件
_洋2 小时前
Three.js 着色器相关方法总结
开发语言·javascript·着色器