琢磨了一下午,在输入输出流上面有了一点点自己的理解。
关于重载运算符读入结构体。
对于一个结构体,如果要读入它我们是不能直接用 cin
的,但是我们可以重载输入流的运算符 >>
,从而使得它可以支持这个操作。
输入流 cin
的数据类型是 istream
。我们不妨把 >>
看成是一个二元运算符,其左操作数是一个 istream
的变量,右操作数是一个需要读入的东西。
不难发现,我们有时候会连写 cin
,如 cin >> a >> b
,注意到 >>
是左结合的,所以可以看成是 (cin >> a) >> b
。可以发现后面的操作符少了一个左操作数,所以 cin >> a
的返回值应该还是 cin
这个输入流变量。
这样我们就有了一个大致的想法,重载运算符 >>
,左操作数读入 istream
,右操作数读入 structName
,读入完之后返回 istream
。即:
1 | istream& operator >> (istream &is, structName &a) { |
如果把这个重载写在全局是没有问题的,但是如果写在结构体内就会 CE。
这是结构体的特性导致的,在重载二元运算符的时候,其第一个参数会默认为它自己(比如在重载矩阵乘法的时候),所以需要加上 friend
标识使得它合法。
即如果把重载写在全局,那么就不需要加 friend
标识,否则必须在重载定义前加。即:
1 | friend istream& operator >> (istream &is, structName &a) { |
类似的,输出也是这么重载。输出流的类型是 ostream
,标识是 cout
。其它规则与输入一样。下面给出一个例子:
1 | friend ostream& operator << (ostream &os, structName a) { |
关于重载运算符的快读。
快读用的时候不太爽,因为如果要读很多个数就要一个一个 read
。考虑用上面的重载运算符优化。
但是如果直接重载 >>,<<
,而且用输入输出流的 istream,ostream
的话,这个快读是无效的,因为 c++
内部的读入就是通过重载实现。
所以考虑自己定义一个读入结构体 Input
。重定义左操作数为结构体 Input
右操作数为整形的 >>
,然后在运算内写快读,即:
1 | class Input { |
在使用的时候,fin
和 cin
是类似的用法。如果需要增加功能,如输入字符串等,就直接新定义 >>
即可。因为根据 c++ template
的性质,函数匹配会优先匹配实参,也就是说如果在这个结构体内还有 inline Input& operator >> (char *s) {}
,然后在输入的时候右操作数是 char*
的话,会优先匹配这个函数。
快输同理:
1 | class Output { |
因为我们这里使用的是 fwrite
,所以在程序结束的时候可能在输出数组中仍然有未输出的字符,这时候就需要 Flush
。但是为了避免在程序结束后忘记 Flush
,我们给 Output
类型的结构体一个析构函数,在程序结束析构它的时候进行 Flush
。
还有一点就是不管是快读还是快输,函数的返回值一定要是 Input/Output &
,即传引用。不然在 return *this
的时候会把这个结构体复制一份返回,这不是我们期望看到的。
但是这样还是有点奇怪,因为 fin
和 fout
我们不习惯写,我们可以 #define cin fin
,#define cout fout
。而且可以 define endl
等。