C/C++程序设计
1
C++ 语言本身没有输入输出函数。 C语言中是通过 FILE描述的结构进行 I/O处理,C++的 I/O类库平行于这个 FILE结构建立一套面向对象的输入输出方式。 FILE结构的实例称为流文件,I/O类的实例称为流对象。
流对象提供两个特点:一个是数据类型安全的自动匹配,另一个是可以对集合类型数据进行输入输出重载。
C/C++程序设计
2
一,iotream类层次概述二、关于 C++的新类三、流对象的输入输出重载函数四、重载流插入运算符和流提取运算符
C/C++程序设计
3
一,iotream类层次概述
ios类是 iotream类的顶层基类,iostream本身直接具有两个基类,因此构成一个多继承的层次结构。
下面是 iotream类的继承树层次。
C/C++程序设计
4
iostream
ostream
ios
istream
istrstream istream_withassign ifstream ostrstreamostream_withassignofstream
strstream stdiostream fstream
图 iotream类继承层次图
C/C++程序设计
5
iotream类继承树上的名称和含义简单地解释如下:
ios 流基类 iostream.h
istream 输入流基类
ifstream 输入文件流类
istream_withassign cin对象的输入流类
istrstream 输入字符串流类
ostream 输出流基类
ofstream 输出文件流类
ostream_withassign cout,cerr和 clog对象的输出流类
iostream 输入输出流基类
fstream 输入输出文件流类
C/C++程序设计
6
stdiostream 标准文件的 I/O流类
strstream 输入输出字符串流类在这个继承树层次之外尚存在一个 streambuf类,负责内存和磁盘文件之间数据的缓冲。
深入细致的编程需要和这个类打交道,本书鉴于篇幅提供的算例未涉及该类。
C/C++程序设计
7
二、关于 C++的新类
C++ 新引进一个 namespace名称空间的概念,原因之一旨在减少程序趋大时名称相同导致的混乱,原因之二是扬弃设计不佳的旧类。扬弃意味发扬固有的长处,抛弃潜在的错误。
这样 I/O类库存在两个略微不同的版本,一个是旧的版本,这个版本不采用 std前导名称分辨符; 一个是新的版本,新的版本相当于将旧的版本扬弃之后平移到 std名称空间。
本课件鉴于旧版本的描述相对简洁,因此采用旧版本的描述说明流类库的使用方法。
C/C++程序设计
8
通过下面两个步骤,可以将旧版本的描述代码转换为新版本的程序:
1,将包含文件指令:
#include<iostream.h>
转换为:
#include<iostream>
新的版本无需文件扩展名,h。
2,在程序中采用 using语句,表明新版本的类在一个独立的 std命名的名称空间中,即将类和对象的名称从 std空间导出:
using namespace std;
C/C++程序设计
9
进行上面两个改动之后,就可以与老版本相同的格式使用新版本的类。
换言之本章的示例和语法可以延拓到新的类。但新的类提供更多的格式标识符和 I/O操作算子。
也可以采用标准名称空间分辨符 std::直接操作 std名称空间的对象和相关的成员函数:
std::cout<<expre; std::cin>>Lexpre;
C/C++程序设计
10
三、流对象的输入输出重载函数
cout定义为 ostream_withassign类的全局对象,这个类派生于 ostream类,下面是 cout全局对象在 vc6.0中的定义的格式,
1,在 ostream.h头文件中予以说明,
extern ostream_withassign cout;
2,仅在另一个实现文件如 iostrini.cpp中初始化一次,
ostream_withassign cout(_new_crt filebuf(1));
这样有效防止全局变量重复定义。
类似地 cin定义为 istream_withassign类的全局对象,
这个类派生于 istream类。
C/C++程序设计
11
左移运算符 <<关于 ostream类进行了重载,这些
operator<<运算符在流的角度称为插入运算符函数,将圆括号中的实参信息插入到 ostream类的数据结构中,即插入信息到 ostream流对象中,例如流对象 cout中。
在 vc6.0 ostream.h中 ostream类的左移运算符函数存在如下函数原型声明:
ostream& operator<<(const char *);
inline ostream&operator<<(const unsigned char*);
inline ostream& operator<<(const signed char*);
ostream& operator<<(unsigned char);
inline ostream& operator<<(char);
inline ostream& operator<<(signed char);
C/C++程序设计
12
ostream& operator<<(short);
ostream& operator<<(unsigned short);
ostream& operator<<(int);
ostream& operator<<(unsigned int);
ostream& operator<<(long);
ostream& operator<<(unsigned long);
inline ostream& operator<<(float);
ostream& operator<<(double);
ostream& operator<<(long double);
ostream& operator<<(const void *);
C/C++程序设计
13
右移运算符 >>关于 istream类进行了重载,istream类的对象代表数据源。这些 operator>>运算符称为提取运算符函数。在流对象如 cin 中提取数据,输送到圆括号中的引用形参指向的实参变量。 scanf函数采用的是指针语法,输入流对象 cin采用的是引用语法。
下面是一些 vc6.0 istream.h中 istream类右移运算符
operator >>函数:
istream& operator>>(short &);
istream& operator>>(unsigned short &);
istream& operator>>(int &);
istream& operator>>(unsigned long &);
istream& operator>>(float &);
istream& operator>>(double &);
C/C++程序设计
14
上面的函数原型都返回对象的引用,这样的运算符函数能够参加连续的运算。例如:
cout<<x<<y<<z; cin>>a>>b>>c;
如果 x 表示只读字符指针,则 cout<<x启动 ostream&
operator<<(const char *)函数。 如果 y表示 long型数据,
启动 ostream& operator<<(long)函数,如此等等。
C/C++程序设计
15
如果 a表示 double型变量,启动 istream & operator>>
(double &)函数,cout<<&a启动 operator<< (const void*)
函数,以十六进制显示地址值,前面加前缀 0x。 cout<<(int)
&a则启动 ostream& operator<<(int)函数,默认地以十进制格式显示地址值。
如果 b表示 int型左值,启动 istream&operator>>(int &)
函数,如此等等。正是这一序列运算符重载函数和类型转换机制构成流对象数据类型的安全的自动匹配原理。
语句 cout<<“ab”<<( void*)“ab”<<endl; 与语句 printf
(“%s0x%p\n”,“ab”,“ab”); 输出相同的结果:
ab0x00426e04。
C/C++程序设计
16
一般地对于 char* p=“ab”; cout<<p显示指针 p指向的字符串,如果要显示指针 p的值可采用格式 cout<<(void*)p 以启动 operator<<(const void*)成员函数,该函数以只读方式显示指针的值。
类型转换 (void*)p不能写为 void*(p)。 cout<<(x+=e)不能写为 cout<<x+=e,因为 cout<<x+=e理解为 (cout<<x)+=e
但 ostream类中没有 operator+=运算符函数。
C/C++程序设计
17
四、重载流插入运算符和流提取运算符上面 operator<< 函数或 operator>>函数的右操作数仅允许是算术类型或指针类型,如果期望右操作数是结构或联合变量或对象 obj,需定义相关的 operator<<或 operator>>
运算符函数,以支持类似于 cout<<obj或 cin>>obj的运算。
由于 ostream和 istream类或其派生类的对象在运算符的左,边相关的提取和插入运算符函数只能是全局函数,其原型分别为:
ostream&operator<<(ostream&out,const CType&r);
istream&operator>>(istream&in,CType&r);
C/C++程序设计
18
这两个重载函数第一个入口参数是流对象的引用,返回的也就是这个对象引用,第二个参数是用户建立的类,用
CType表示。第二个参数优先采用引用形参。如果这个
CType类存在私有成员需要访问,则将上面两个声明平移到类中,声明为 friend函数:
class CType { friend ostream & operator<<
( ostream & out,const CType& r) ;
friend istream & operator>>
( istream & in,CType& r) ; };
它们具有定义格式:
Ostream&operator<<(ostream&ov,const CType&r)
{;...;语句序列 ; return ov; }
C/C++程序设计
19
istream & operator>>(istream & iv,CType & r)
{ ;...;语句序列 ; return iv; }
[例 ] 定义插入运算符和提取运算符函数
# include<iostream.h>
class CType
{ int x,y;
public,long m,n;
CType (int i,int j) { x=i; y=j; }
friend ostream & operator<<
(ostream & out,const CType & ) ;
};
C/C++程序设计
20
Ostream & operator<<(ostream & out,const CType & r)
{ out<<"[x,y]="<<r.x<<","<<r.y<<endl;
out<<"[m,n]="<<r.m<<","<<r.n<<endl;
return out;
}
Istream & operator>>(istream & in,CType & r)
{ in>>r.m>>r.n; return in; }
void main()
{ CType obj (100,200);
cout<<"Please Enter 2 Integer:";
cin>> obj; cout<<obj;
}
C/C++程序设计
21