本人在OI中很长时间(直到现在)是cstdio党;也就是说很不习惯用stl,虽然我也不知道具体的原因。
不过一直用scanf、printf是很麻烦的。比如下面的A+B问题:
#include<cstdio> int main(){ int a,b; scanf("%d%d",&a,&b); printf("%d",a+b); return 0; }
如代码所示,每次都要用%格式符格式化,不写不行;scanf还要额外用取址符&,不用不行。这样是很麻烦的!有时候会想如果cin、cout没有那么慢的话,在OI里可能就用了,毕竟很方便:
#include<iostream> int main(){ int a,b; cin>>a>>b; cout<<a+b; return 0; }
说到速度,OI里有时候会用读入输出优化:
#include<cstdio> int read(){ int x=0,w=1,c=getchar(); while(c<'0'||c>'9'){ if(c=='-') w=0; c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+(c-'0'); c=getchar(); } return w?x:-x; } void write(int a){ if(a<0) putchar('-'),a=-a; if(a>9) write(a/10); putchar(a%10+'0'); return; } int main(){ int a,b; a=read(); b=read(); write(a+b); return 0; }
本人一直有着这种关于cin、cout的想法。后来在紫书中看lrj实现BigInt类,重载了<<和>>。那之后我就查了stl的输入输出流是怎么写的。于是今天就想着自己实现一个输入输出流,这样可能会很方便。
iostream中关于输入输出有两个基类:标准输入流(class istream)和标准输出流(class ostream)。这两个流分别重载了>>运算符和<<运算符。还有一些派生类,比如fstram、sstream之类的。
拿istream举例。istream重载>>运算符用于输入流。每次读入一个变量,再返回一个流。重载的伪代码如下:
class istream{ public: //以int举例 istream operator >> (int &a){ //读入整型并赋值给a(伪代码) return *this; } };
最后return一个流,这样可以连续地读入(像cin>>a>>b一样)。顺便一提,这个重载实际写成了virtual,为了使fstream之类的重载。顺便一提,这个代码不是从stl里copy的,只是本蒟蒻yy出来的……不过思路差不多。
有了这个思路,我们就可以写输入流了!为了解决伪代码里的读入整型的问题,同时解决cin的速度问题,我们可以使用读入优化!
代码如下:
class istream{ //以int举例 public: int read(){ int x=0,w=1,c=getchar(); while(c<'0'||c>'9'){ if(c=='-') w=0; c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+(c-'0'); c=getchar(); } return w?x:-x; } istream operator >> (int &a){ a=read(); return *this; } };
输出流是一样的道理~
代码如下:
class ostream{ //以int举例 public: void write(int a){ if(a<0) putchar('-'),a=-a; if(a>9) write(a/10); putchar(a%10+'0'); return; } ostream operator << (const int &a){ write(a); return *this; } };
这样我们就可以像cin、cout一样读入、输出整数了。
字符串也可以这样搞。如果是单个字符,可以用getchar()、putchar()来写。如果是字符串就这么搞:
class istream{ public: void getstr(char a[]){ int i=0; char c; do{ c=getchar(); a[i++]=c; }while(c!=EOF&&c!=26&&c!=0&&c!=' '&&c!=' '&&c!=' '); //这里判断是否已经读入完。EOF是文件输入时的文件结束符 //加上EOF可以兼容freopen。26是控制台输入^Z的值,其实 //^Z和EOF是一样的,只不过EOF的值是-1,如果输入不以回车 //或空格结束,那么不加26这个判断就不会结束控制台读入。 a[i-1]=0; //这一步将串的最后置为0,表示串到此结束。 //scanf也是这么操作的。 return; } istream operator >> (char a[]){ getstr(a); return *this; } }; class ostream{ public: void write(const char a[]){ int i=0; while(a[i]!=0) putchar(a[i++]); return; } ostream operator << (const char a[]){ write(a); return *this; } };
这样搞支持freopen重载之后的文件输入输出,但不支持fopen的方式。
这样我们就可以用流的方式输入输出字符和字符串了!
整数和字符的完整输入输出流(注:这里把两个流写在一起,不需要特别注意分别输入和输出的情况下效果是一样的):
#include<cstdio> class IO{ public: int getint(){ int x=0,w=1,c=getchar(); while(c<'0'||c>'9'){ if(c=='-') w=0; c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+(c-'0'); c=getchar(); } return w?x:-x; } long long getlong(){ long long x=0; int w=1,c=getchar(); while(c<'0'||c>'9'){ if(c=='-') w=0; c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+(c-'0'); c=getchar(); } return w?x:-x; } void getstr(char a[]){ int i=0; char c; do{ c=getchar(); a[i++]=c; }while(c!=EOF&&c!=26&&c!=0&&c!=' '&&c!=' '&&c!=' '); a[i-1]=0; return; } void write(int a){ if(a<0) putchar('-'),a=-a; if(a>9) write(a/10); putchar(a%10+'0'); return; } void write(long long a){ if(a<0) putchar('-'),a=-a; if(a>9) write(a/10); putchar(a%10+'0'); return; } void write(const char a[]){ int i=0; while(a[i]!=0) putchar(a[i++]); return; } virtual IO operator >> (int &a){a=getint();return *this;} virtual IO operator >> (long long &a){a=getlong();return *this;} virtual IO operator >> (char &a){a=getchar();return *this;} virtual IO operator >> (char a[]){getstr(a);return *this;} virtual IO operator << (const int &a){write(a);return *this;} virtual IO operator << (const long long &a){write(a);return *this;} virtual IO operator << (const char &a){putchar(a);return *this;} virtual IO operator << (const char a[]){write(a);return *this;} }get,put;
最后为这个IO类实现两个实例:get和put,就可以当成加速的cin和cout了(如果不用stl可以直接实现成cin和cout,用着习惯)。
如果自己实现了一个什么其他的类,也可以重载一个运算符,这样就可以用在这个类上了。比如:
class Pair{ friend IO operator >> (IO in,Pair &a); friend IO operator << (IO out,const Pair &a); private: int f,s; public: Pair(int ff=0,int ss=0){ f=ff; s=ss; } }; IO operator >> (IO in,Pair &a){ return in>>a.f>>a.s; } IO operator << (IO out,const Pair &a){ return out<<'('<<a.f<<','<<a.s<<')'; }
stl里的流也可以这样重载,只不过参数的流和返回的流都要加引用。
最后一提,这里面没有实数,但是实数的读入优化也是可以写的,有兴趣可以自己试一试。只不过cout实数是像printf的%g一样,自动判断小数点后有效位数(去除后导0),这个暂时不知道怎么写……
谁还记得我写这个的初衷是想要写着方便来着……