《重学C++》7. C++高级语法(二)IO操作
主要内容
这一篇主要总结自第七章”C++高级语法”中的第7-14节, 包含面向对象编程的IO部分,主要有:重载输入输出运算符、IO流和两种文件的IO操作(读写文件)。
1. 虚数类的实现——重载输入输出运算符
C++三种限制访问符:
- private:私有的,只能在类内使用,子类和类外不可以使用
- protected:保护的,类内和子类可以使用,类外不可以使用
- public:公有的,类内和类外、子类都可以使用
重载输入输出运算符并不是通过成员函数的方式,而是要利用到友元函数,只有通过friend关键字,才可以访问内部变量。
除了注意要写friend关键字外,因为友元函数并不是成员函数,所以不需要类作用域限定符。
1 | //下面两行是声明,应当写到类内 |
注:为什么一定要用到友元函数?
实现输入输出会使用到对象内部的private变量,要想有读取/写入private变量的权限,有两种途径:1.成员函数,2.友元函数。
如果是重载双目操作符(即为类的成员函数),就只要设置一个参数作为右侧运算量,而左侧运算量就是对象本身。。。。。。
而 >> 或<< 左侧运算量是 cin或cout 而不是对象本身,所以不满足后面一点。。。。。。。。就只能申明为友元函数了。。。
如果一定要声明为成员函数,只能成为如下的形式:
ostream & operator<<(ostream &output)
{
return output;
}
所以在运用这个<<运算符时就变为这种形式了:data<<cout;
不合符人的习惯。
为什么operator<<运算符重载一定要为友元函数呢?
https://www.cnblogs.com/this-543273659/archive/2011/09/01/2161574.html
2. IO流概述
简单来说,C++中各种I/O的stream结构如下:
实际使用中,主要使用iostream、fstream、sstream,分别对标准输入输出流、文件流、字符串流进行读写操作。(其中输入流ifstream和istringstream都继承自istream,输出流类似)
缓存区
不管是哪种流,一般都会采用缓冲区。缓冲区分为三种:按块缓存、按行缓存和不缓存。按块缓存一次将数据读入,例如读取文件、按行缓存逐次读入数据,例如while从cin读取数据:
如下代码,while循环不断读取一个整数,收到第5个时退出循环,循环外部再接收一个字符
1 | int a; |
当一次性输入1 2 3 4 5 6时,这6个数据应该只被while读入5个,剩下一个应当无法被读取。但实际运行中,程序却将其读入了循环外的代码部分:
这种情况被叫作读入了缓存区脏数据。
这是因为输入1 2 3 4 5 6时,程序将其一次全部读入了缓存区。后面循环外的代码直接从缓存区读取了第6个输入,也就是脏数据。要想解决这种情况,必须在输入回车之后清空缓存区中的脏数据。
可以在最后一次读取字符的代码前加入如下代码:
cin.ignore(numeric_limitsstd::streamsize::max(), ‘\n’); // 清空缓存区脏数据
3. 文件IO
文件按类型分类:
- 文本文件,文字等信息经过编码(如ASCII码等)变成无意义的二进制代码,存放在内存中。
- 二进制文件,二进制代码就是文本内容(两者相同,不需要编码)。实际项目中接触的大多数文件
通过fstream读写文件,一般流程:
- 打开文件open;
- 检测是否打开成功,没有的话fail不能使用;
- 对文件read/write;
- 检测文件是否读完(EOF);
- 使用完文件关闭close。
文件打开方式(这些文件模式定义在std::ios命名空间):
文件模式 | 功能 |
---|---|
in | 只读方式打开 |
out | 写方式打开 |
app | 每次写操作前均定位到文件末尾(追加) |
ate | 打开并立即定位到文本末尾 |
trunc | 打开并清除文件内容(默认) |
binary | 二进制方式进行IO |
如果文件不存在,默认不会打开/创建文件。
判断文件是否打开成功,应该使用fstream对象的fail()函数;判断文件是否读完,应该使用fstream对象的eof()函数。所有fstream相关函数见http://www.cplusplus.com/reference/fstream/fstream/
读写二进制文件部分,自己练习的代码