《重学C++》7. C++高级语法(二)IO操作

主要内容

这一篇主要总结自第七章”C++高级语法”中的第7-14节, 包含面向对象编程的IO部分,主要有:重载输入输出运算符、IO流和两种文件的IO操作(读写文件)。




1. 虚数类的实现——重载输入输出运算符

C++三种限制访问符:

  • private:私有的,只能在类内使用,子类和类外不可以使用
  • protected:保护的,类内和子类可以使用,类外不可以使用
  • public:公有的,类内和类外、子类都可以使用
    重载输入输出运算符并不是通过成员函数的方式,而是要利用到友元函数,只有通过friend关键字,才可以访问内部变量。

除了注意要写friend关键字外,因为友元函数并不是成员函数,所以不需要类作用域限定符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//下面两行是声明,应当写到类内
//friend ostream& operator<<(ostream& os, const Complex& comp);
//friend istream& operator>>(istream& is, Complex& comp);

//输入运算符重载
ostream& operator<<(ostream& os, const Complex& comp) {
os << "The real value is " << comp.real << ", the image value is " << comp.image;
return os;
}
//输出运算符重载
istream& operator>>(istream& is, Complex& comp) {
is >> comp.real >> comp.image;
return is;
}

注:为什么一定要用到友元函数?
实现输入输出会使用到对象内部的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结构如下:

来源:http://www.cplusplus.com/reference/sstream/stringstream/
如果详细一点,可以如下图所示: ![](https://raw.githubusercontent.com/ipdor/Pictures/master/img/IOclasses.png)
来源:https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp10_IO.html

实际使用中,主要使用iostream、fstream、sstream,分别对标准输入输出流、文件流、字符串流进行读写操作。(其中输入流ifstream和istringstream都继承自istream,输出流类似)

缓存区

不管是哪种流,一般都会采用缓冲区。缓冲区分为三种:按块缓存、按行缓存和不缓存。按块缓存一次将数据读入,例如读取文件、按行缓存逐次读入数据,例如while从cin读取数据:

如下代码,while循环不断读取一个整数,收到第5个时退出循环,循环外部再接收一个字符

1
2
3
4
5
6
7
8
9
10
11
12
13
int a;
while (cin >> a)
{
cout << "The numbers are: " << a << endl;
index++;
if (index == 5)
{
break;
}
}
char ch;
cin >> ch;
cout << "the last char is: " << ch << endl;

当一次性输入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

文件按类型分类:

  1. 文本文件,文字等信息经过编码(如ASCII码等)变成无意义的二进制代码,存放在内存中。
  2. 二进制文件,二进制代码就是文本内容(两者相同,不需要编码)。实际项目中接触的大多数文件

通过fstream读写文件,一般流程:

  1. 打开文件open;
  2. 检测是否打开成功,没有的话fail不能使用;
  3. 对文件read/write;
  4. 检测文件是否读完(EOF);
  5. 使用完文件关闭close。

文件打开方式(这些文件模式定义在std::ios命名空间):

文件模式 功能
in 只读方式打开
out 写方式打开
app 每次写操作前均定位到文件末尾(追加)
ate 打开并立即定位到文本末尾
trunc 打开并清除文件内容(默认)
binary 二进制方式进行IO

如果文件不存在,默认不会打开/创建文件。
判断文件是否打开成功,应该使用fstream对象的fail()函数;判断文件是否读完,应该使用fstream对象的eof()函数。所有fstream相关函数见http://www.cplusplus.com/reference/fstream/fstream/

读写二进制文件部分,自己练习的代码