在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. docker 在 centos7.* 上的部署及问题解决

    最近尝试搭建docker 环境,其实个人是比较喜欢“菜鸟学习系列”的知识的,怎奈它的讲解是以Ubuntu为主的,最后找到一个搭建学习系列,感觉写的很好,主要是页面风格清晰明了,遂决定按照此教程学习搭建 ...

  2. 使用 LVS 实现负载均衡原理及安装配置详解(课堂随笔)

    一.负载均衡LVS基本介绍 LB集群的架构和原理很简单,就是当用户的请求过来时,会直接分发到Director Server上,然后它把用户的请求根据设置好的调度算法,智能均衡地分发到后端真正服务器(r ...

  3. 各种软件安装的URL

    Python爬虫动态抓取的工具 PhantomJS

  4. mysql 报错ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2)

    ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ ( ...

  5. JIRA数据库的迁移,从HSQL到MYSQL/Oracle

    Jira数据库迁移,从HSQL到MYSQL 通过JIRA管理员登录,进入“管理员页面”,“系统”--“导入&导出”,以XML格式备份数据. 在MySQL中创建Schema,命名为jira 关闭 ...

  6. Wine 总结

    下诉描述有些问题,我用非root用户安装的软件有些也会安装到root用户的家目录里不知道为什么:[我知道了,貌似用了sudo的安装的都是在root目录里..] 经过测试,最好用root权限安装,否则会 ...

  7. hadoop 集群调优实践总结

    调优概述# 几乎在很多场景,MapRdeuce或者说分布式架构,都会在IO受限,硬盘或者网络读取数据遇到瓶颈.处理数据瓶颈CPU受限.大量的硬盘读写数据是海量数据分析常见情况. IO受限例子: 索引 ...

  8. Request部分知识点小结

    HTTP: * 概念:Hyper Text Transfer Protocol 超文本传输协议 * 传输协议:定义了,客户端和服务器端通信时,发送数据的格式 * 特点: 1. 基于TCP/IP的高级协 ...

  9. Python尾递归优化

    Python开启尾递归优化 cpython本身不支持尾递归优化, 但是一个牛人想出的解决办法:实现一个 tail_call_optimized 装饰器 #!/usr/bin/env python2.4 ...

  10. pandas之时间序列笔记

    时间戳tiimestamp:固定的时刻->pd.Timestamp 固定时期period:比如2016年3月份,再如2015年销售额->pd.Period 时间间隔interval:由起始 ...