C++之自定义的output缓冲区

来源:互联网 发布:汉文化复兴 知乎 编辑:程序博客网 时间:2024/05/29 13:21

转自:http://hi.baidu.com/nicker2010/item/661c9bad742dd9a228ce9deb

tream缓冲区是一种I/O缓冲区,其接口由basic_streambuf<>定义

    缓冲区的接口主要由三个指针构成。
    函数eback(),gptr(),egptr()返回的指针构成了read(即input)缓冲区的界面。
    函数pbase(),pptr(),epptr()返回的指针构成了write(即output)缓冲区的界面。

    1.pbase()(意思是"put base")是output stream缓冲区的开始位置
    2.pptr()(意思是"put pointer")是当前写入位置
    3.epptr()(意思是"end put pointer")是是output stream缓冲区的结尾,指向最后一个字符的下一个位置

    pbase()和pptr()之间的序列字符(不包括pptr()指向的字符)已被写到相应的输出通道,但是尚未清空(flush)

    成员函数sputc()可以写入一个字符:
    如果有空位置,字符就复制到该位置,然后pptr()的指针加1
    如果缓冲区没有空位置供写入,则会调用overflow()函数将缓冲区的内容发送到相应的通道中

    成员函数sputn()可以用来一次写入多个字符。其内部实际上将任务委派给内部函数xsputn()
    后者可针对多个字符做更多有效的操作。
    通常,同时写入多个字符会比一次写入一个效率高得多,因此可以用sputc()来优化对字符序列的处理

   对一个缓冲区写数据时不一定的采取缓冲操作,可以令指令一写入就到达(如cerr),
    此时default构造函数会自动将维护write缓冲区的指针设为NULL(0)

   没有采取缓冲行为的stream buffer,每个字符到达都会调用overflow()函数
    下面就是一个没有采取缓冲行为的自定义stream buffer示例

/**
每一个发送到stream buffer的字符,都转换成大写再以函数putchar()写入
*/
class OutBuf : public std::streambuf
{
protected:
    virtual int_type overflow(int_type c)
    {
        if(c != EOF)
        {
            c = std::toupper(c,getloc());//getloc()取得与stream对象相关的locale对象
            if(putchar(c) == EOF)
                return EOF;
        }
        return c;
    }
};

/**
上面这个自定义的stream buffer其实是特别针对char实现的,
因为std::streambuf的定义就是:typedef basic_streambuf<char> streambuf

如果还用到其他字符性别,如wchar_t,则必须运用字符特性来实现这些函数
*/
template <class charT,class traits = std::char_traits<charT> >
class Basic_OutBuf : public std::basic_streambuf<charT,traits>
{
protected:
    virtual typename traits::int_type overflow(typename traits::int_type c)
    {
        if(!traits::eq_int_type(c,traits::eof()))
        {
            c = std::toupper(c,std::basic_streambuf<charT,traits>::getloc());
            if(putchar(c) == EOF)
                return traits::eof();
        }
        return traits::not_eof(c);
    }
};

///Basic_OutBuf的简单使用:
typedef Basic_OutBuf<char> outbuf;
typedef Basic_OutBuf<wchar_t> woutbuf;

void testBasicOutBuf()
{    outbuf buffer;
    ostream out(&buffer);

    out<<"2011 hexadecimal:"<<std::hex<<2011;

}

此方法可以用于对其他任意目标的写入,
例如stream缓冲区可以接受一个文件描述符、一个socket连接、或两个stream缓冲区(用于在对象初始化时同步写入)
若要想目标端改写数据,只要实现overflow()函数,最好也实现xsputn(),这样有助于提升效率

下面定义了一个以文件描述符初始化的stream缓冲区
文件描述符的字符由write()函数写入

extern "C"
{
    int write(int fd,const char* buf,int num);
}
class fdOutBuf : public std::streambuf
{
protected:
    int fd;
public:
    fdOutBuf(int _fd):fd(_fd){}
protected:
    virtual int_type overflow(int_type c)
    {
        if(c != EOF)
        {
            char z = c;
            if(write(fd,&z,1) != 1)
            {
                return EOF;
            }
        }
        return c;
    }
    virtual std::streamsize xsputn(const char* s,std::streamsize num)
    {
        return write(fd,s,num);
    }
};

class fdostream : public std::ostream
{
protected:
   fdOutBuf buf;  ///使用自定义的stream缓冲区
public:
    fdostream(int fd):std::ostream(0),buf(fd)
    {
        rdbuf(&buf);
    }
};

void testfdostream()
{
    fdostream out(1);/**通常1为标准输出通道*/
    out<<"2011 hexadecimal:"<<std::hex<<2011;
}

如果要实现具备缓冲能力的stream缓冲区,
write()函数必须调用setp()初始化

代码如下:

class outputbuf : public std::streambuf
{
protected:
    static const int buffersize = 10;
    char buffer[buffersize];
public:
    outputbuf()
    {
        setp(buffer,buffer+(buffersize-1));
    }
    /**保证stream缓冲区被销毁时,缓冲区的数据仍会被写入目标介质中*/
    virtual ~outputbuf()
    {
        sync();
    }
protected:
    int flushBuffer()/**清空缓冲区*/
    {
        int num = pptr() - pbase();
        if(write(1,buffer,num) != num) /**将缓冲区内的内容输出到标准输出通道*/
            return EOF;
        pbump(-num); /**当前指针移动到流的来时位置*/
        return num;
    }
    /**只要尚余一个字符空间,就会调用overflow*/
    virtual int_type overflow(int_type c)
    {
        if(c != EOF)
        {
            *pptr() = c;/**字符写入当前位置*/
            pbump(1);
        }
        if(flushBuffer() == EOF)
        {
            return EOF;
        }
        return c;
    }
    /**造成缓冲区的当前状态和相应的介质同步
    唯一需要做的就是清空缓冲区
    没有缓冲操作的stream缓冲区,没有必要清空缓冲区,
    所有也没必要重载这个虚函数
    */
    virtual int sync()
    {
        if(flushBuffer() == EOF)
        {
            return -1;
        }
        return 0;
    }
};


C++流这一块感觉比较底层,很多东西都比较陌生。确实比较难哈!