手动实现iostream

Posted Halifuda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手动实现iostream相关的知识,希望对你有一定的参考价值。

本人在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;
}
A+B(cstdio)

如代码所示,每次都要用%格式符格式化,不写不行;scanf还要额外用取址符&,不用不行。这样是很麻烦的!有时候会想如果cin、cout没有那么慢的话,在OI里可能就用了,毕竟很方便:

技术分享图片
#include<iostream>
int main(){
     int a,b;
     cin>>a>>b;
     cout<<a+b;
     return 0;
}
A+B(iostream)

说到速度,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;
}
A+B(优化)

本人一直有着这种关于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!=\n&&c!=\r); 
        //这里判断是否已经读入完。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!=\n&&c!=\r); 
            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<<);
}
Pair重载流

stl里的流也可以这样重载,只不过参数的流和返回的流都要加引用。

最后一提,这里面没有实数,但是实数的读入优化也是可以写的,有兴趣可以自己试一试。只不过cout实数是像printf的%g一样,自动判断小数点后有效位数(去除后导0),这个暂时不知道怎么写……

谁还记得我写这个的初衷是想要写着方便来着……

以上是关于手动实现iostream的主要内容,如果未能解决你的问题,请参考以下文章

如何在按下单个片段的手动后退按钮时返回上一个片段?

C++ 代码片段执行

是否可以在片段中手动调用 onCreateView?

Ace Editor 手动添加片段

手写数字识别——基于全连接层和MNIST数据集

Android YouTube Player API Fragment无法手动处理触摸事件