浅谈C++ IO流

1.输入输出(IO)与流的概念

输入输出(IO)是指计算机同任何外部设备之间的数据传递。常见的输入输出设备有文件、键盘、打印机、屏幕等。数据可以按记录(或称数据块)的方式传递,也可以 流的方式传递。

所谓记录,是指有着内部结构的数据块。记录内部除了有需要处理的实际数据之外,还可能包含附加信息,这些附加信息通常是对本记录数据的描述。

流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出。

C++IO流,特指以流的方式进行输入输出的ISO/ANSI标准C++库的输入输出类库,也就是专门负责处理IO操作的一套系统。任何需要传递的数据,都要经过这套系统的处理。

2.数据的表示形式

IO操作的过程中,任何需要被传递的数据,在经过IO类库处理前后是不同的。这样,我们可以把数据的表示分为两种:内部表示和外部表示。

数据的内部表示便于程序进行数据处理。典型的内部表示有:整型数的二进制表示、浮点数的IEEE表示、字符的ASCII或Unicode编码表示。数据的外部表示则根据不同的外部设备的需要,有具体不同的表现形式。如果外部数据表示是可读的字符序列,则称为文本IO,否则为二进制IO。标准IO流的主要目的是支持文本IO,不直接支持二进制IO。

虽然IO流是以流的方式进行数据传递,但这并不表明传递的数据不能有任何结构,而是指IO流的概念是以流的方式进行输入输出,所传递数据的内部结构隐藏在对流数据的解释中。

3.IO的步骤

在IO流里,输入输出分为4步:格式化/解析,缓冲,编码转换和传递。

格式化/解析:在内部数据表示(以字节为单位)与外部数据表示(以字符为单位)之间进行双向转换。例如一个2字节的整数10002,就需要5个字符来表示。

缓冲:用于在格式/解析与传递只加缓存字符序列。对于输出,较短的字符序列格式化之后并不马上输出,而是保存在缓冲区里,待累积到一定规模之后再传递到外部设备。相反,从外部设备读入的大量数据也是先放在缓冲区,然后逐步取出完成输入。默认时,IO流的输入输出都是经过缓冲的,也可以让IO流工作在无缓冲模式下。

编码转换: 是将一种字符表达式转换成另一种字符表达式。如果格式化产生的字符表达式与外部字符表达式不同(输出时),或者外部表达式与IO流能解析的表达式不同(输入时),就必须进行编码转换。如多字节编码与宽字符编码之间的转换等。多数情况下并不需要进行编码转换。

传递:主要是与外部设备进行通信。输出时,传递负责将经过格式化、缓冲即编码转换后的字符序列发送到外部设备;输入时,则负责将外部设备抽取数据,为其后进行的编码转换、缓冲及解析提供字符序列。

4.IO流类库的组成结构

IO流类库在不同平台的具体实现上,可能会有所变化,但从总体设计上来看,C++流库主要由两个流类层次组成:

(1)以streambuf类为父类的类层次

主要完成信息通过缓冲区的交换。派生层次如下:

缓冲区:是一个队列数据结构,由一字符序列和两个指针组成,这两个指针分别指向字符要被插入或被取出的位置。
streambuf类为所有的streambuf类层次对象设置了一个固定的内存缓冲区,动态划分为两部分:
用做输入的取区,用取指针指示当前取字符位置。
用做输出的存区,用存指针指示当前存字符位置。

(2)以ios类为父类的类层次

ios类及其派生类是在streambuf类实现的通过缓冲区的信息交换的基础上,进一步增加了各种格式化的输入/输出控制方法。它们为用户提供使用流类的接口,它们均有一个指向streambuf的指针。

ios类有四个直接派生类:

  1. istream
  2. ostream
  3. fstreambase
  4. strstreambase

这四种流作为流库中的基本流类。ios类的派生层次如下:

5. IO流类库的优点

C++语言开发了自己的IO流类库,用以取代C语言的基本输入输出函数族。对于有经验的C程序员来说,C语言提供的IO函数库时有效且方便的。但是,C语言的IO函数库有其自身的缺点,特别是在C++这种面向对象的程序设计语言中,C语言函数库无法直接支持面向对象的程序设计。因此,C++语言开发自己的IO流类库是必然的。具体来说,IO流类库具有以下优点。

(1)简明与可读性

IO流类库用IO运算符(提取运算符>>和插入运算符<<)代替了不同的输入输出函数名,如printf和scanf等。从直观来看,这种改变使得IO语句更为简明。另外,也减轻了程序员在记忆函数名和书写程序上的一些负担。例如:

printf(“n=%d,a=%f\n”,n,a);
cout<<”n=”<<n<<”,a=”<<a<<endl;

虽然两条语句的输出结果是一样的,但是后者更加简明,直观,易写,易读。

(2)类型安全(type safe)

所谓类型安全,是指编译器所理解的数据实体(如变量。指针所指向的数据等)的类型,与实际数据实体的实际类型或对该数据所进行的操作之间保持一致性。在进行IO操作时,编译器将自动检查实参的表达式类型来调用IO流类相应的重载版本的成员函数,来完成输入输出。而采用C的IO函数,必须显示指明操作的数据类型,如采用printf()函数,由于其参数中的数据类型必须由程序员以参数格式%d,%f,%c,%s,容易出错。

(3)易于扩充

C++语言的IO流类库,是建立在类的继承关系、模板和操作符重载等机制的基础上的。把原来C语言中的左、右移位运算符<<和>>,通过运算符重载的方法,定 义为插入(输出)和提取(输入)运算符。这就为输入输出功能对于各种用户定义的类型数据的扩充,创造了方便的条件。

用户可以采用输入输出操作符的重载来完成用户想要的输入输出功能。例如,用于复数类Complex的输出操作符重载函数可以定义为:

friend ostream& operator<<(ostream& s,const Complex& c){
 s<<c.real<<"+"<<c.image<<"i"<<endl;
 return s;
}

输入输出操作符有个固定的格式,以上是一种常用的格式。由于C语言并不支持函数重载,也不直接支持面向对象的程序设计,所以想扩充C语言的输入输出函数使它们支持用户定义的新数据类型,是一件非常困难的事情。

以上就是浅谈C++ IO流的详细内容,更多关于C++ IO流的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解C++ sizeof(下)

    sizeof作用于基本数据类型,在特定的平台和特定的编译器中,结果是确定的,如果使用sizeof计算构造类型:结构体.联合体和类的大小时,情况稍微复杂一些. 1.sizeof计算结构体 考察如下代码: struct S1 { char c; int i; }; cout<<"sizeof(S1)="<<sizeof(S1)<<endl; sizeof(S1)结果是8,并不是想象中的sizeof(char)+sizeof(int)=5.这是因为结构体或

  • C++ 使用new与delete需注意的原则

    C++的动态内存管理是通过new和delete两个操作来完成的,即用new来申请空间,用delete来释放空间.在使用new和delete时,注意以下原则. 1.new与delete需一一对应 用new操作申请空间,如果申请成功,必须在以后的某个时刻用delete释放该空间,既不能忘记释放,也不能多次释放.前者会引起内存泄露,后者会引起运行时错误.如下面的程序. #include <iostream> using namespace std; int main() { int *p; p=ne

  • 详解C++纯虚函数与抽象类

    1.虚函数 1.1虚函数简介 虚函数可以毫不夸张的说是C++最重要的特性之一,我们先来看一看虚函数的概念. 在基类的定义中,定义虚函数的一般形式为: virtual 函数返回值类型 虚函数名(形参表) { 函数体 } 为什么说虚函数是C++最重要的特性之一呢,因为虚函数承载着C++中动态联编的作用,也即多态,可以让程序在运行时选择合适的成员函数.虚函数必须是类的非静态成员函数(且非构造函数),其访问权限是public.那么:  (1)为什么类的静态成员函数不能为虚函数?  如果定义为虚函数,那么

  • C++枚举类型enum与enum class的使用

    一.关于枚举类型 1. 什么是枚举类型? 答:如果一个变量只有几种可能的值,那么就可以定义为枚举类型,比如:性别只有男和女,那么就可以将性别定义为一种枚举类型,其中男和女就是性别所包含的变量.所谓"枚举"是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内.在C++中,枚举类型分为不限定作用域(enum)和限定作用域(enum class). 2. enum与enum class的区别? (为什么需要限定作用域?) 答:枚举作用域是指枚举类型成员名字的作用域,起自其声明之处,

  • c++代码调试方式的几点建议

    1.代码调试的重要性 代码调试在程序开发阶段占有举足轻重的地位,可见代码调试的重要性.但是有一点必须强调:程序是设计出来的,而不是调试出来的.这是所有程序员必须牢记在心的一条准则.一个没有设计或者这几得很糟糕的程序,无论怎样调试,也不会成为一个合格的程序. 程序有着良好的设计的前提下,软件开发的过程中,编码错误在所难免.所有程序可能出现的错误可分为两类:语法错误和逻辑错误.调试通常是指在消除了语法错误之后,发现程序中的逻辑错误的过程.对C/C++程序进行调试,有这样集中常用的手段.它们既可以单独

  • 详解C++ sizeof(上)

    sizeof是C/C++中的一个操作符(operator),其作用是返回一个对象或者类型所占的内存字节数,使用频繁,有必须对其有个全面的了解. 1.sizeof的基本语法 sizeof有三种语法形式. (1)sizeof(object); //sizeof(对象); (2)sizeof(type_name); //sizeof(类型); (3)sizeof object; //sizeof对象; 第三种语法结构虽然简约,但并不常见,为简单统一,建议使用第一和第二种写法. int i; sizeo

  • VS Code C/C++环境配置教程(无法打开源文件“xxxxxx.h” 或者 检测到 #include 错误,请更新includePath) (POSIX API)

    一.问题描述与分析 编辑C/C++程序,我推荐使用C/C++,VS Code相对于别的编译器来说有很多的优势.但是如果第一次使用的话,会觉得其不好用.因为如果不配置好的话,操作会比较麻烦. 注意:我这里是在windows下编写Linux程序. 例如在使用VS Code编辑C/C++程序在没有配置好的情况下,会出现如下图情况, 出现这种情况的原因是 在VS Code没有找到头文件.或者是VS Code没有配置好. 为了很好的解释上述的问题,请先了解下Cygwin.MinGW.POSIX等,并了解下

  • c++禁止函数的传值调用的方法

    代码编译运行环境:VS2017+Debug+Win32 按照参数形式的不同,C++应该有三种函数调用方式:传值调用.引用调用和指针调用.对于基本数据类型的变量作为实参进行参数传递时,采用传值调用与引用调用和指针调用的效率相差不大.但是,对于类类型来说,传值调用和引用调用之间的区别很大,类对象的尺寸越大,这种差别越大. 传值调用与后面两者的区别在于传值调用在进入函数体之前,会在栈上建立一个实参的副本,而引用和指针调用没有这个动作.建立副本的操作是利用拷贝构造函数进行的.因此,要禁止传值调用,就必须

  • c++ 面向对象设计五大原则

    面向对象设计(OOD)是面向对象编程(OOP)必不可少的一个环节,只有好的设计,才能保障程序的质量.面向对象设计的主要任务就是类的设计,不少面向对象(OO)的先驱和前辈已经提出了很多关于类的设计原则,用于指导OOP,其中就包括类设计的五项基本原则. 1.单一职责原则(Single Resposibility Principle,SRP) 专注是一个人的优良品质,同样,单一职责也是一个类的优良设计.单一职责的核心思想:一个类只做好一件事情. 单一职责原则可以看作是高内聚.低耦合在面向对象原则上的引

  • 简述C++的复杂性

    1. C++真的很复杂吗 这个问题的答案是肯定的.从C++语言本身的发展和组成来看,C++语言并不是一种单一."纯粹"的编程语言,他有着复杂的内部结构. 最初,C++仅仅是在C的基础上附加了一些object-oriented(面向对象)的特性.C++最初的名字是"C with Class".以后C++不断的创新和发展,融入了procedural(过程化),object-oriented(面向对象),functional(函数化),generic(泛型)以及metap

随机推荐