在C++ 中引入了流的概念,我们很方便的通过流来读写文本数据和二进制数据,那么流对象的数据究竟是怎么存储的呢,为了搞清这个问题,先来看一看c++ 的 io 体系:

由图可以看出,在stream 的实现中,除了虚基类IOS_BASE之外,所有的类内部都有一个streambuf, streambuf 是一个虚基类(不能被实例化,因此所内部包含streambuf(这个虚基类而非其子类)的类也是虚基类),代表流对象内部的缓冲区,就是我们流操作中输入输出的内容在内存中的缓冲区。

Streambuf有两个子类,分别是stringbuf 和 filebuf,这两个子类可以被实例化,我们常用的文件流和字符串流,内部的缓冲区就是这两个类。

我们平常使用到的流基本是标准输入输出流,文件流和字符串流。在每个流初始化的时候都会初始化相应的streambuf(其实是它的子类)用来缓冲数据。

当我们用文件或者字符串初始化流的时候,流内部会保存该文件和字符串的信息,而在内部实例化一个streambuf用来缓冲数据,些数据时,当缓冲区满的时候再将数据写到文件或者字符串,读数据时当缓冲区没有数据时从文件或字符串读数据到缓冲区。

在文件流这种情况下,streambuf 是为了避免大量的IO 操作

在字符串流的情况下,streambuf (其实是套在上面的流对象)是为了提供字符串的格式化读取和输出操作(想象字符串是你从键盘输入的数据)

所以streambuf 可以看作一块缓冲区,用来存储数据,在这种情况下,我们常常在程序中用的 char数组缓冲区是不是可以被替代呢?答案是of course

而且,有了streambuf ,缓冲区的管理和写入写出都非常方便,最好的是流对象有复制拷贝等构造函数可以方便参数传递等需要拷贝的情景。

但是streambuf 本身是个虚基类,不能实例化,所以要用streambuf 就需要自己继承streambuf 写一个新的类出来才能用,这个实现方法最后介绍,好在c++ 标准类库实现了两个子类stringbuf 和 filebuf ,所以我们可以选stringbuf 来作为我们的数据缓冲对象(不选filebuf 是因为它的实现和文件紧耦合的,只适合文件流)

流对象有一个构造函数是通过streambuf 来构造:

    1. stringbuf sb;
    2. istream is(&sb);

有了流对象我们就可以在流上进行各种输入输出操作,输入会从缓冲区读数据,输出会将数据写到缓冲区

注意对缓冲区的读写一定要注意方法,流符号是格式话输入输出,get,put,read,write等是二进制读写。

格式化输入的内容应当格式化读取,二进制写入应当二进制读取否则会出现写入和读出数据不一致的问题

格式化写入一个int 数据时,会将该数据每位分离出来,按照字符编码写到缓冲区,例如 int x= 123, 格式化写入以后缓冲区存以后,缓冲区有三个字节分别存放1,2,3的字符编码。格式化读出是相反的过程,将读到的字符转成相应的类型的数据

二进制写入时进行直接的内存拷贝不做任何动作,例如 int x = 123 二进制写入后(二进制写时需要取地址,转成char* 并指出要写入的字节数,如f.write((char*)&x,sizeof(int))

写完后缓冲区的数据是0x0000007b,是计算机内存中对123 的内存的完全拷贝

下面是缓冲区使用的情景:

考虑一个生产者,消费者的问题,线程A 生成的数据,线程B读取,可以解决的方案如下:

1. 设立全局变量,缓冲数据,A,B都可以访问(在这种情况下,A 生产的时候要考虑缓冲区是否够用,B读取的时候要判断当前是否有有效数据可读,而且很难设计一个合理分配内存的缓冲区(想象A生产的数据有时很大,有时很小))

2.网络通信(TCP,UDP)

3. streambuf 登场,有了streambuf配合stream,  A就像正常操作流一样往流对象里塞数据,而B 就像正常操作流一样从流里面读数据,不用关心其他问题,只要这两个流的sterambuf 是同一个对象。

上一段代码:
————————————————

#include <iostream>
#include <streambuf>
#include <sstream>
#include <fstream>
#include <string>
#include <cstring>
#include <memory>
#include <thread>
using namespace std;
stringbuf buf;
istream in(&buf);
ostream out(&buf);
bool flag = false;
void threadb() {
char data;
while (true) {
if (flag) {
in >> data;
cout << "thread B recv:" << data << endl;
flag = false;
}
}
}
int main() {
thread consumer(threadb);
char data;
while (true) {
cin >> data;
out << data;
flag = true;
}
return ;
}

在特殊的情景下可以实现自己的streambuf类,自己实现的类必须继承streambuf 类,自定义的streambuf 必须实现overflow,underflow,uflow 等方法,其中overflow在输出缓冲区不够用时调用,underflow和uflow在输入缓冲区无数据时调用,区别是uflow 会让读取位置前进一位,而underflow不会。sreambuf 内部维护着六个指针 eback,gptr,egptr,pbase,pptr,epptr.分别指向读取缓冲区的头,当前读取位置,尾,写缓冲区的头,当前写位置,尾(实际上这几个指针指向同一段缓冲区)

自定义实现方式要注意要在该返回EOF的时候,返回EOF,underflow和uflow都有可能返回EOF,一旦返回了EOF则标志着流结束,之后对流的操作无效。

如下代码实现了一个自定义的streambuf:

#include <iostream>
#include <streambuf>
#include <sstream>
#include <fstream>
#include <string>
#include <cstring>
#include <memory>
using namespace std;
class mybuf : public streambuf {
public:
enum{ SIZE = };
mybuf() {
memset(buffer, 'j', );
//buffer[3] = ' ';
setbuf(buffer, SIZE);
}
void log() {
cout <<hex<<gptr() << endl;
}
protected:
int_type overflow( int_type c) {
cout << "overflow" << endl;
return c;
}
streambuf* setbuf(char* s, streamsize n) {
setp(s, s + n);
setg(s, s, s + n);
return this;
}
int_type underflow() override{
cout << "here"<<endl;
memset(buffer, 'w', );
setg(buffer, buffer, buffer+);
return ' ';
}
int_type uflow() override{
cout << "uflow" << endl;
memset(buffer, 'x', );
setg(buffer, buffer, buffer + );
return EOF;
}
private:
char buffer[SIZE];
};
int main() {
mybuf buf;
char test[];
memset(test, 'a', );
//buf.pubsetbuf(test, 1000);
string hh;
string xx;
istream in(&buf);
ostream tt(&buf);
in>>hh;
cout << hh << endl;
//tt.write(test, 9);
in >> xx;
in.read(test, );
cout<< xx << endl;
cout << "end" << endl;
return ;
}

c++ 流对象之streambuf(可当做缓冲区使用)的更多相关文章

  1. C++之把流对象当做函数参数传递

    一.编译不通过的代码: /******************************************************************************* * File ...

  2. C++文件流类与文件流对象

    文件流是以外存文件为输入输出对象的数据流.输出文件流是从内存流向外存文件的数据,输入文件流是从外存文件流向内存的数据.每一个文件流都有一个内存缓冲区与之对应. 请区分文件流与文件的概念,不用误以为文件 ...

  3. 【JAVA 其它流对象】

    一.PrintStream类. 该流是字节流. public class PrintStream extends FilterOutputStream implements Appendable, C ...

  4. C++学习47 文件的概念 文件流类与文件流对象 文件的打开与关闭

    迄今为止,我们讨论的输入输出是以系统指定的标准设备(输入设备为键盘,输出设备为显示器)为对象的.在实际应用中,常以磁盘文件作为对象.即从磁盘文件读取数据,将数据输出到磁盘文件.磁盘是计算机的外部存储器 ...

  5. Java之IO流基础流对象

    输入流和输出流是相对于内存设备而言 即将外设中的数据读取到内存中就是输入    将内存中的数据写入到外设中就是输出   字符流的由来:     其实就是:字节流读取文字字节数据后,不直接操作而是先查指 ...

  6. java IO之 序列流 集合对象Properties 打印流 流对象

    序列流 也称为合并流. SequenceInputStream 序列流,对多个流进行合并. SequenceInputStream 表示其他输入流的逻辑串联.它从输入流的有序集合开始,并从 第一个输入 ...

  7. Java基础---Java---IO流-----对象的序列化、管道流、RandomAccessFile、数据类型的流对象DataStream、ByteArrayStream

    ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化. ObjectOutputStream 和 ObjectInputStream ...

  8. Java基础-IO流对象之内存操作流(ByteArrayOutputStream与ByteArrayInputStream)

    Java基础-IO流对象之内存操作流(ByteArrayOutputStream与ByteArrayInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.内存 ...

  9. Java基础-IO流对象之字符缓冲流(BufferedWriter与BufferedReader)

    Java基础-IO流对象之字符缓冲流(BufferedWriter与BufferedReader) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.字符缓冲流 字符缓冲流根据流的 ...

随机推荐

  1. 避免复制引用程序集的XML文件

    VS在编译时,默认会复制所有引用程序集对应的XML文件到输出目录. 在项目中设置AllowedReferenceRelatedFileExtensions可以避免复制操作. <PropertyG ...

  2. Andrew Ng机器学习课程9-补充

    Andrew Ng机器学习课程9-补充 首先要说的还是这个bias-variance trade off,一个hypothesis的generalization error是指的它在样本上的期望误差, ...

  3. mysql navcat备份使用详解

    mysql navcat备份使用详解 点击备份 然后新建备份 然后选择要备份的表 就可以了 以后这个表删除了 内容变更了 都可以点击 还原备份就可以了

  4. LeetCode 1022. 从根到叶的二进制数之和(Sum of Root To Leaf Binary Numbers)

    1022. 从根到叶的二进制数之和 1022. Sum of Root To Leaf Binary Numbers 题目描述 Given a binary tree, each node has v ...

  5. Lombok - 使用注解让你的JavaBean变得更加简洁

    Lombok - 工具简介: Lombok是一个编译时注释预处理器,有助于在编译时注入一些代码.Lombok提供了一组在开发时处理的注释,以将代码注入到Java应用程序中,注入的代码在开发环境中立即可 ...

  6. Python32之类和对象2(self参数及魔法方法)

    一.类方法中的self参数含义 在Python中类的方法都要有self参数,其实质为对类的实例化对象的绑定从而使得在类的实例化对象调用方法时能够确认出是对哪个对象进行操作. 带self的的参数是人家实 ...

  7. 题解 Luogu P1099 【树网的核】

    这题是真的水啊... ------------ 昨天模拟赛考了这题,很多人都是O($n^3$)水过,但我认为,要做就做的足够好(其实是我根本没想到O($n^3$)的做法),然后就开始想O(n)的解法. ...

  8. C++开发新版本vs使用旧版本vs编译的静态库动态库

    关于vs潜在的升级问题概述 (Visual C++)查看官网的介绍:潜在的升级问题概述 (Visual C++).主要问题: 1. 如果使用 /GL(全程序优化)进行编译,则生成的对象文件只能使用生成 ...

  9. Python-05-字符串格式化

    一.百分号方式 %[(name)][flags][width].[precision]typecode (name)      可选,用于选择指定的key flags          可选,可供选择 ...

  10. PPPoE中间人拦截以及校园网突破漫谈

    本文首发于PPPoE中间人拦截以及校园网突破漫谈,转载请注明出处. PPPoE中间人拦截以及校园网突破漫谈 校园生活快结束了,之前还有点未完成的想法,趁着这两天有兴趣搞搞. 此文面向大众是那种在校园内 ...