一、关于流的概念和一些基本关系
1.流的概念
流(stream)在C++中指字节流,其实就是1010这样的二进制,以字节为单位在程序和外部流动。打个比方,流可以看成一个官职,名叫河道监管,负责管理河道的水流流向。水流既可以流向田地,灌溉桑田,也可以流向江河湖泊。输入流就是让数据从外部(键盘、文件、内存、等)流向程序,输出流就是让数据从程序流出到外部(屏幕、文件、打印机等)。
2.基本关系
首先需要明确几个常见的类:ios类(模板类),ios_base类(非模板类),ostream类,istream类,iostream类。他们的关系还是可以用河道监管这个职位来描述。首先,皇帝建立了河道监管这个职位,需要派人上任。第一任河道监管的名字叫做ios_base类。后来,ios_base类把位置传给了它儿子,也就是ios类。ios类子承父业,并且修建了一座水库便于调水。不过ios类觉得自己干太累,于是他建立了几个新官职,一个负责管理水库(缓冲区内存),即streambuf类。一个负责正向调水(输出),一个负责反向调水(输入),也就是ostream类和istream类。后来,ostream和istream类为了把自己的调水技能传承下去,决定联姻,生出了一个既能正向调水,也能反向调水的人才,叫做iostream类。下图来自C++ Primer Plus一书。
从用途分类,C++中常用的有三种流,一种是用于控制台的流(istream, ostream, iostream),也就是常见的从键盘输入,屏幕输出的流。上述例子就是说的这个。此外,还有文件流(ifstream, ofstream, fstream)用于文件读写、以及字符串流(istringstream,ostringstream, stringstream)常用于实现字符串到其他数据类型的转换。
上图不是很完整,比如ofstream类是ostream类的派生类,ifstream类是istream类的派生类。fstream类是iostream类的派生类。
接下来从这三种流的角度出发,分别说明其重点。
二、三流重点
2.1 输入输出流(iostream)
对象:
iostream类是一个类,既然是类,就要用类创建对象。包含<iostream>头文件后,系统会创建8个对象,分别是cin、cout、cerr、clog以及wcin、wcout、wcerr、wclog。后四个与前四个作用类似,不同点在于后者处理的数据类型为wchar_t类型。
· cin对象对应于标准输入流,默认情况下关联到标准输入设备(键盘)
· cout对象对应于标准输出流,默认情况下关联到标准输出设备(显示器)
· cerr对象对应于标准错误流,用于显示错误信息。默认情况下关联到标准输出设备(显示器)。这个流没有缓冲区,信息将直接发送给屏幕而不是等着你敲回车
· clog对象与cerr对象类似,也对应于标准错误流,默认情况下关联到标准输出设备(显示器),但有缓冲区。
成员函数:
iostream类继承了istream类和ostream类,所以需要知道这两类中常用的一些函数。
·ostream类:
① 重载运算符<<,可用于所有基本的数据类型,但如果是自定义类型需要重载后使用。
② put()和write()函数。前者用于显示字符,后者用于显示字符串。
·istream类:
① 重载运算符>>,可用于所有基本的数据类型,但如果是自定义类型需要重载后使用。
②get()和getline(),用于获取字符。两者都可以遇到换行符或者到指定最大字符时停止。区别在于get将换行符保留在缓冲区,getline将其丢弃。
③ ignore(),用于抛弃字符。
④ 其他不常用的:read(),用于读字符;peek(),用于查看下一个字符;gcount(),用于返回最后一个非格式化抽取方法读取的字符数(不是由抽取操作符(>>)读取的);putback()用于将一个字符插入到输入字符串中。被插入的字符是下一条语句读取的第一个字符。
cin:
从键盘读取数据,遇到空格、换行符结束。cin不会丢弃换行符,也就是把换行符留在了缓冲区,但当cin>>从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符
如果遇到类型不匹配的字符,cin会读取失败并返回0;
cin.get()和cin.getline():
cin.get()中,括号里不加任何东西,作用是只读一个字符,即使是一个换行符。
cin.get(char),读取一个字符。
cin.get(字符串地址,大小),读一行字符,到换行或最大个数时停止。
注:get函数不会丢弃换行符,会将其留在缓冲区。
cin.getline(字符串地址,大小),读一行字符,到换行或最大个数时停止。
注:cin.getline函数会丢弃换行符,不会将其留在缓冲区。
注:getline(cin,str)是string类的函数,跟cin.getline不一样。
优缺点:getline的优点是不会在缓冲区留下东西,但缺点是无法确定是读完一行遇到换行符停止还是到达字符数量上限停止。get更适用于判断是否读完一行。也就是说可以在读完一行后加一个get()函数,如果get()读取到了换行符,说明get函数完整的读完一整行。
cout:
console output(控制台输出)。cout用起来比较简单,但在搜查相关资料的时候,发现了一个很有趣的问题。先看这个看似简单的题:
int i=1;
cout<<++i<<i++<<i<<i++<<++i<<endl;
这两行代码会输出什么?我刚开始想,很简单嘛,2,2,3,3,5。但是不同的编译器结果不一样。Visio Studio和g++编译的结果是53525,VS code编译的结果是22335。为什么会产生53525的结果?首先,这个结果说明cout连续输出时是从右往左处理数据的,并且把数据放在类似于栈的结构中。这样,结果就变成了53322。但是还有一个点,i++和++i的区别。++i和i共用一个地址,但i++返回一个新地址(可以自己写一个程序观察一下内存地址的区别)。因此,最终输出的++i和i应该是相等的,结果应该是53525。
调整字段宽度:cout.width(int i);(只影响接下来一个)
填充字符:cout.fill(‘*’);(接下来的都有效)
设置精度:cout.presision(精度);(接下来的都有效,且精度指小数点后面的位数)
setf()函数,输出格式设置。有多种形式,用的时候自己看。
2.2 文件流(fstream)
ofstream 程序写入文件
使用方法:
① 创建ofstream对象
② 与文件关联 如:对象.open(“文件名”) 注:open()和close()要成对用
③ 像用cout一样用创建好的对象
ifstream的使用方法同理。
流状态检查:is_open()
二进制输入输出:read()、write()
2.3 字符串流(sstream)
https://www.bilibili.com/video/BV13r4y1w7nC?spm_id_from=333.880.my_history.page.click&vd_source=08e671d7e4b88d1170c01942ff4e0dd6
三、边角料
1.河道监管只负责水流的流向,不负责水流从哪儿来,到哪儿去。至于具体的来源去向,还是要听上级的指挥。这种相对独立的思想是C++中常见的一种思想。
2.一般来说不带.h的C++标准库头文件都需要用到命名空间std。流也一样,std就相当于朝廷,不论河道监管还是手底下的cin、cout等都属于朝廷指定的官职,必须打着朝廷的名号用。所以在使用时需要声明:我是朝廷的命官!
写的不是很完整,因为我也是初学者。但写都写了 等用到学到新东西再进行补充。