【破烂集】大佬的封装模板——输入输出

说实话在看了很多大佬的代码后(不管有没有理解),给人的第一个直观印象就是——他们的代码为什么看着和我们的风格相差那么大,用到了很多很多的类的封装和define,鉴于他们是大佬并且已经身经百战了,所以应该可以认为那些代码可能是包括他们,甚至是他们的前人很多人实战中经验的积累和智慧的结晶,所以我们应该尝试着去思考他们那些代码的好处是什么,我也想了很多,现在看来的确是好处多多。我就随便说几点我看到的,当我们在使用模板的时候,我们常常会因为很多变量名的重复以及互相覆盖而花很多时间修改,而且一个模板包括变量函数等等很多东西,都必须要交叉着存放,还需要注意重名的问题,这个在当一道题需要用到很多不同的算法模板的时候格外突出,所以这样封装的好处就非常明显了,因为如果你把东西都封装起来,你会发现当你需要使用一个模板的时候只是轻轻松松copy与paste的过程,而且完全不需要担心重名和函数变量交错混乱的问题,所有的模板整齐第一个一个排列粘贴即可,这个对于很多复杂的问题真的太重要了,所以我也决定重新好好看看cpp primer类的那些章节。当然,我们第一次看到一个大佬的题解的时候,我们可能会觉得非常的复杂,这个时候是最需要耐心的,因为如果我们能够慢慢将那些密集而复杂的代码抽丝剥茧般地一点一点解读出来的时候,我们不但理解了那些代码,我们还能或得一些新的思想和技巧,而且用起模板(特别是出现错误的时候)能够更加得心应手。

说了这么多,我也决定先介绍一下这次的这个模板,这个应该是一位出题级别大佬的,我花了4个小时解读,终于算是理解了这个输入输出,非常的神奇,也非常的强大。
解读我都写在了注释里面。

# include <cstdio>
# include <cstring>

//封装的输入挂 
/*
L是2097153,相当于先用一个这么长的字符串当做流并且用fread和fwrite快速输入输出(这个比较类似gets大法,但fread比gets好,因为gets是只能读入一行,但是这个可以任意读入,直到eof,另外实测这两个东西速度差不多) 

fread
fread是一个函数,它从文件流中读数据,最多读取count个项,
每个项size个字节,如果调用成功返回实际读取到的项个数(小于或等于count),
如果不成功或读到文件末尾返回 0。

char ibuf[L],*iS,*iT,obuf[L],*oS=obuf,*oT=obuf+L-1,c,st[55];int f,tp;
ibuf是一个模拟流的容器,iS和iT是这个输入流的首尾指针,obuf是输出流,oT代表可以存放元素的最后地址的后一个(本身不会存放(虽然位置合法)),oS代表当前空余最左的位置 


解读#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,L,stdin),(iS==iT?EOF:*iS++)):*iS++)//ps:这行代码是整个程序输入的核心精髓 
if(iS == iT)
{
    iT = (iS = ibuf)+ fread(ibuf , 1 , L , stdin);//将IT初始化成为最后的eof(类似于结束符,没有信息量),Is初始化为开头 
    iS == iT ? EOF : *iS++;//IS和IT相同有两种情况,一种是最开始他们还没初始化,还有一种是流全部输完了,如果是后一种,那么再次fread返回0,所以IT和IB全部变为开头,依然相等 
    //如果是第一种,那么此时不同,所以把开头的值返回 
    //还有一种可能,就是这个流没有输入所有的元素,所以这时会再次输入进一个L,因为L足够大,所以应该不会出现因为完整性爆炸的情况(感觉最长的输入应该就是字符串了,但是超过2e6的字符串输入不多见)
    //总结一下,就是通过fread一个L一个L地把输入的流汇入模拟流 
}
else
    *iS++;//流的中途,直接返回 

结论:gc就是一个人造的getchar,但是因为使用了更快的fread,所以优于getchar,基本达到了gets大法的程度,而且普适性更强 

fwrite
fwrite() 是 C 语言标准库中的一个文件处理函数,功能是向指定的文件中写入若干数据块,如成功执行则返回实际写入的数据块数目。
该函数以二进制形式对文件进行操作,不局限于文本文件。 

flush与getc 
//ps:这两个函数是整个程序输出的核心精髓
flush相当于把所有在流里面的东西 
直接解释一下具体过程吧,他们把所有的输出全部储存再这个流里面,只有当流存满的时候才进行一次flush,
也就是一次性frite把流输出干净,然后继续输入进去 

总结:这两个函数的组合很类似与putchar,但是更加高效 

剩下的函数全部都是基于这两个函数的实现,感觉跟经典的那些挂差不多,
这个挂的优势除了封装全部在于上面两个超级优化的putchar,getchar,所以这些无关紧要 

解读struct IOFLUSHER{ ~IOFLUSHER() { flush(); } } _ioflusher_;
struct IOFLUSHER
{ 
    ~IOFLUSHER() { flush(); }
}_ioflusher_;

我相信看明白之前那个输出流的人肯定一直都会有一个疑问,就是如果在输出流当中残存了元素没有填满的话怎么办,
这时候应该在最后的时候把残留的元素输出干净,然而鉴于这个程序肯定是对的,而且只剩下了这个奇怪的东西,
所以我觉得肯定就是这个东西起了作用,结果也果然如此
首先解释一下~A()这种函数是什么,它叫构析函数,隶属于一个类之中(class或struct),这个东西通俗一点可以称之为“亡语”
(玩过炉石的都懂),也就是说它会在这个类的元素被系统解构(作为分函数变量在分函数运行完,或者作为全局变量或者主函数变量在程序运行完,或者直接被delete)
 之后发动,所以这里就是构造了这样一个类,并且声明了一个这样的全局变量,所以在程序结束的时候,这个家伙的亡语,
 也就是这个构析函数会被触发,也就是flush了一次 

总结:很高级的函数,通过对输入输出流的模拟使用fwrite和fread这对最快输入输出组合的确是妙不可言 
*/


//输入输出挂套装 
namespace io {
    const int L=(1<<21)+1;
    char ibuf[L],*iS,*iT,obuf[L],*oS=obuf,*oT=obuf+L-1,c,st[55];int f,tp;
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,L,stdin),(iS==iT?EOF:*iS++)):*iS++)
    inline void flush() {
        fwrite(obuf,1,oS-obuf,stdout);
        oS=obuf;
    }
    inline void putc(char x) { *oS++=x; if (oS==oT) flush(); }
    template<class I> inline void gi(I&x) {
        for (f=1,c=gc();c<'0'||c>'9';c=gc()) if (c=='-') f=-1;
        for (x=0;c<='9'&&c>='0';c=gc()) x=x*10+(c&15); x*=f;
    }
    template<class I> inline void print(I x) {
        if (!x) putc('0');
        if (x<0) putc('-'),x=-x;
        while (x) st[++tp]=x%10+'0',x/=10;
        while (tp) putc(st[tp--]);
    }
    inline void gs(char*s, int&l) {
        for (c=gc();c<'a'||c>'z';c=gc());
        for (l=0;c<='z'&&c>='a';c=gc()) s[l++]=c;
        s[l]=0;
    }
    inline void ps(const char*s) { for (int i=0,n=strlen(s);i<n;i++) putc(s[i]); }
    struct IOFLUSHER{ ~IOFLUSHER() { flush(); } } _ioflusher_;
};
using io::putc; 
using io::gi;
using io::gs;
using io::ps;
using io::print;
//

int main()
{


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值