面向对象程序设计071输入输出流对象课件.ppt
面向对象程序设计,七、输入/输出流对象,内容提要,螺旋矩阵的输入输出问题 流无格式输入/输出输入/输出的格式控制自定义类型对象的输入输出文件的输入/输出问题,第7章输入/输出流对象,2022/12/19,3,提出问题,编程输出M*M的螺旋矩阵,要求数据间距与M相同。例如,4*4的螺旋矩阵,其间距为4: 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 7,第7章输入/输出流对象,2022/12/19,4,分析问题,C+没有定义任何用于执行输入或输出的语句,但可以使用C语言中的标准I/O函数printf()和scanf()来解决此问题。,第7章输入/输出流对象,2022/12/19,5,【例】有如下程序:#include int b=20;float a=5.6;int main() printf(%dn,a); scanf(%d,该程序的运行结果为:161061273623454349980,第7章输入/输出流对象,2022/12/19,6,rintf()与scanf()存在以下两个缺点:1非类型安全2不可扩充性class AA a;printf(%?,a); /不知用什么格式符来识别A的对象,第7章输入/输出流对象,2022/12/19,7,C+用I/O流来解决这个问题。下面先介绍流的概念。,第7章输入/输出流对象,2022/12/19,8,流的概念,在C+程序中,数据可以从外部设备(键盘等)流入到计算机内存中,也可以从计算机内存流向外部设备(屏幕或磁盘文件等),这种数据传递被称作输入输出。C+将数据从一个位置到另一个位置的传递抽象为流。所谓流(stream)就是指数据从一个位置流向另一个位置。流是字节的序列。C+的输入输出操作是基于流来处理的。,第7章输入/输出流对象,2022/12/19,9,I/O流操作,C+基于流的概念处理数据的输入输出,因此也称之为输入输出流,即I/O流。I/O流具有两个基本的行为特征,一是从流中获取数据的操作称为提取操作,二是向流中添加数据的操作称为插入操作。,第7章输入/输出流对象,2022/12/19,10,I/O流类,C+标准库围绕流的概念,提供了一整套I/O流类簇,既可用于标准类型数据的I/O,也能用于自定义类型对象的I/O,这种扩展性是C+最有价值的特点之一。C+标准库提供的I/O流类的各种操作都是类型安全(type safe)的。不同类型的I/O流操作都是重载的,没有定义过I/O功能的类型不具备I/O操作的能力。,第7章输入/输出流对象,2022/12/19,11,I/O流类簇的层次结构,C+标准库提供的I/O流类簇有两个平行基类:streambuf(filebuf,stringbuf,strstreambuf)ios(istream,ostream)ios类有下面直接派生类:输入流类istream(ifstream,istringstream,istrstream)输出流类ostream(ofstream,ostringstream,ostrstream)(iostream-fstream,stringstream,strstream),第7章输入/输出流对象,2022/12/19,12,I/O流分类,C+标准库提供的I/O流按功能可分为如下3类:标准I/O流:内存与标准输入输出设备(键盘、屏幕)之间数据的传递;文件I/O流:内存与外部磁盘文件之间数据的传递;字符串I/O流:内存变量与表示字符串流的字符数组之间数据的传递。,第7章输入/输出流对象,2022/12/19,13,标准I/O流对象,标准I/O流预定义了4个流类对象:cin、cout、cerr、clog。操作系统给把外设用文件名的方式进行管理,因此程序设计要访问外设就跟访问文件一样方便。,第7章输入/输出流对象,2022/12/19,14,I/O分为无格式支持的低级I/O和具有格式支持的高级I/O。无格式支持的低级I/O以基本字节为操作对象,有格式支持的高级I/O把若干字节组合成有意义的单位,如整数、浮点数、字符、字符串及用户自定义类型等等。,第7章输入/输出流对象,2022/12/19,15,无格式输入/输出,无格式输入/输出就是按系统预定义的格式进行的输入/输出。按默认约定,每个C+程序都能使用标准I/O流,如标准输入、标准输出。cin用来处理标准输入,即键盘输入;cout用来处理标准输出,即屏幕输出。它们被定义在iostream头文件中。在使用cout和cin前,要用编译预处理命令将所使用的头文件包含到源程序中,其格式如下:#include ,第7章输入/输出流对象,2022/12/19,16,无格式输出,“”是预定义的插入运算符,作用在流类对象cout上,实现默认格式的屏幕输出。使用cout输出表达式值到屏幕上的格式如下:coutE1E2Em;其中,E1、E2、Em为均为表达式。功能是计算各表达式的值,并将结果输出到屏幕当前光标位置处。,第7章输入/输出流对象,2022/12/19,17,无格式输出,cout是ostream流类的对象,它在iostream头文件中作为全局对象定义,其格式如下:ostream cout(stdout);其中,stdout表示标准输出设备名(屏幕)。在ostream流类中,对应每个基本数据类型定义运算符“”重载函数为友元,它们在ostream中声明: ostream,第7章输入/输出流对象,2022/12/19,18,无格式输入,“”是预定义的提取运算符,作用在流类对象cin上,实现默认格式的键盘输入。使用cin将数据输入到变量的格式如下:cinV1V2Vn; 其中,V1、V2、Vn都是变量。功能是暂停执行程序,等待用户从键盘输入数据,各数据间用空格或Tab键分隔,输入数据类型要与接受变量类型一致,输完后,按回车键结束。,第7章输入/输出流对象,2022/12/19,19,无格式输入,cin是istream流类的对象,它在iostream头文件中作为全局对象定义,其格式如下:istream cin(stdin);在istream流类中,对应每个基本数据类型定义运算符“”重载函数为友元,它们在istream中声明:istream ,第7章输入/输出流对象,2022/12/19,20,输入输出的格式控制,C+提供了两种格式化输入/输出方式:一种是用ios类成员函数进行格式化输入输出另一种是用专门的操作符函数进行格式化输入输出,第7章输入/输出流对象,2022/12/19,21,用ios类成员函数格式化,ios类成员函数主要是通过对状态标志、输出宽度、填充字符以及输出精度的操作来完成输入/输出格式化。输入输出格式由各种状态标志来确定,这些状态标志在ios类中定义为枚举量。由于该枚举量定义在ios类中,因此引用时必须包含ios:前缀。使用时应该全部用符号名,绝不要用数值。,第7章输入/输出流对象,2022/12/19,22,enumskipws = 0 x0001 ,/跳过输入空格left = 0 x0002 ,/按左对齐格式输出right = 0 x0004 ,/按右对齐格式输出internal = 0 x0008 ,/输出符号和基指示符后的填补dec = 0 x0010 ,/转换为十进制(In / Out)oct = 0 x0020 ,/转换为八进制(In / Out)hex = 0 x0040 ,/转换为十六进制(In / Out)showbase = 0 x0080 ,/输出显示基指示符showpoint = 0 x0100 ,/输出显示小数点uppercase = 0 x0200 ,/大写十六进制输出showpos = 0 x0400 ,/正整数显示前加上“”scientific = 0 x0800 ,/输出用科学表示法表示的浮点数fixed = 0 x1000 ,/输出用固定小数点表示的浮点数unitbuf = 0 x2000 ,/在输出操作后刷新所有流stdio = 0 x4000/在输出操作后刷新stdout和stderr这些标志可以由ios类成员函数访问,也可以用操作符函数访问。,第7章输入/输出流对象,2022/12/19,23,用ios成员函数对状态标志进行操作,ios类有3个成员函数可以对状态标志进行操作,分别为flags()、setf()和unsetf(),并且ios类还定义了一个long型数据成员记录当前状态标志。这些状态标志可用位或运算符“|”进行组合。用setf函数设置状态标志,其一般格式如下:long ios:setf(long flags),第7章输入/输出流对象,2022/12/19,24,用ios成员函数对状态标志进行操作,用unsetf函数清除状态标志,其一般格式如下:long ios:unsetf(long flags)用函数flags取状态标志有两种形式,其一般格式分别如下:long ios:flags() long ios:flags(long flag)第一种形式返回与流相关的当前状态标志值;第二种形式将流的状态标志值设置为flag,并返回设置前的状态标志值。,第7章输入/输出流对象,2022/12/19,25,例 使用ios成员函数操作状态字void showflags(long f);void main()long f;f=cout.flags(); /取当前状态标志showflags(f); /显示状态值cout.setf(ios:showpos|ios:scientific|ios:fixed); /追加状态标志f=cout.flags(); /取当前状态标志showflags(f); /显示状态值cout.unsetf(ios:scientific); /从状态标志中去掉标志f=cout.flags(); /取当前状态标志showflags(f); /显示状态值f=cout.flags(ios:hex); /重新设置状态标志showflags(f);f=cout.flags(); /取当前状态标志showflags(f);,第7章输入/输出流对象,2022/12/19,26,用ios成员函数设置输出宽度,设置输出宽度函数有两种形式,其一般格式分别如下:int ios:width(int len)int ios:width()第一种形式是设置输出宽度,并返回原来的输出宽度;第二种形式是返回当前输出宽度,输出宽度为0。,第7章输入/输出流对象,2022/12/19,27,例 使用width控制输出宽度#include using namesoace std;int main() double values = 1.23,35.36,653.7,4358.24; for(int i=0;i4;i+) cout.width(10); cout valuesi n; ,输出结果: 1.23 35.36 653.7 4358.24,第7章输入/输出流对象,2022/12/19,28,用ios成员函数设置填充字符,填充字符的作用是当输出值不满输出宽度时用填充字符来填充,默认填充字符为空格。它与width()函数配合使用,否则没有意义。设置填充字符函数有两种形式,其格式分别如下:char ios:fill(char ch)char ios:fill()第一种形式是重新设置填充字符,并返回设置前的填充字符;第二种形式是返回当前的填充字符。,第7章输入/输出流对象,2022/12/19,29,例 使用fill设置填充字符#include using namesoace std;int main() double values = 1.23,35.36,653.7,4358.24; for(int i=0;i4;i+) cout.width(10); cout.fill(*); cout valuesi n; ,输出结果:*1.23*35.36*653.7*4358.24,第7章输入/输出流对象,2022/12/19,30,用ios成员函数设置输出精度,设置浮点数输出精度有两种形式,其格式分别如下:int ios:precision(int p)int ios:precision()第一种形式是重新设置输出精度,并返回设置前的输出精度;第二种形式是返回当前的输出精度。,第7章输入/输出流对象,2022/12/19,31,例 使用precision设置填充字符#include using namesoace std;int main() int j;j=cout.precision();coutpresicion:jendl;cout.precision(8);cout123.456789endl;coutpresicion:cout.precision()endl;,输出结果:precision:6123.45679precision:8,第7章输入/输出流对象,2022/12/19,32,用操作符函数格式化输入输出,除了专门的ios类成员函数,c+标准库还提供了标准的操作符函数专门操控这些状态。这组函数不属于任何类成员,定义在iomanip.h头文件中。将它们用在提取运算符“”或插入运算符“”后面来设定输入/输出格式,即在读写对象之间插入一个修改状态的操作。其中有些函数没有参数,所以又叫操作符。,第7章输入/输出流对象,2022/12/19,33,设置输入/输出宽度函数setw(int)设置输出填充字符函数setfill(int) 设置输出精度函数setprecision(int)设置输入/输出整型数数制函数dec、hex和oct 取消输入结束符函数ws 控制换行操作符endl 代表输出单字符“0”的操作符ends,第7章输入/输出流对象,2022/12/19,34,例 使用setw设置输入/输出宽度#include #include using namesoace std;int main() char *p=12345,*q=678;char f4,g4; /最后一位为0int i=10;coutsetw(4)fg; /设置输入宽度coutfendlgendli:iendl; return 0;,运行结果:12345 678123456781234512345i:10,第7章输入/输出流对象,2022/12/19,35,例 分别用浮点、定点的方式表示一个实数#include #include using namesoace std;int main() double f=22.0/7;/在用浮点表示的输出中,setprecision(n)表示实数有效位数coutfendl; /默认有效位数为6coutsetprecision(3)fendl; /最小的有效位数为3/在用定点表示的输出中,setprecision(n)表示小数有效位数coutsetiosflags(ios:fixed); coutsetprecision(8)fendl; /小数位数为8return 0;,运行结果:3.142863.143.14285714,第7章输入/输出流对象,2022/12/19,36,例 使用dec、hex、oct设置输出整型数制#include #include using namesoace std;int main() int number=1001; coutDecimal:decnumberendl Hexadecimal:hexnumberendl Octal:octnumberendl;return 0;,运行结果:Decimal:1001Hexadecimal:3E9Ocatl:1751,第7章输入/输出流对象,2022/12/19,37,用户自定义操作符函数,c+提供了标准的操作符函数,也提供了建立自定义操作符函数的方法。建立输出操作符函数的格式如下:ostream 其中manip_name是自定义操作符函数的名字。,第7章输入/输出流对象,2022/12/19,38,例 用户自定义输出操作符函数#include #include using namesoace std;ostream,运行结果:10 10&,第7章输入/输出流对象,2022/12/19,39,用户自定义操作符函数,建立输入操作符函数的格式如下:istream 其中manip_name是自定义操作符函数的名字。,第7章输入/输出流对象,2022/12/19,40,例 用户自定义输出操作符函数#include #include using namesoace std;istream,运行结果:Enter number using hex format:ff255,第7章输入/输出流对象,2022/12/19,41,自定义类型对象的输入/输出,上面都是对基本类型数据进行输入/输出操作,实际上,C+允许对自定义类型对象进行同样简单语法的输入/输出。示例 自定义类型对象的输入/输出,第7章输入/输出流对象,2022/12/19,42,class Datepublic: Date(int y,int m,int d) Year=y;Month=m;Day=d;friend ostream,第7章输入/输出流对象,2022/12/19,43,int main()Date date(2005,11,28);coutdate;coutNew date:dateendl;return 0;输出结果为:Current date:2005/11/28Enter new date:2007 12 9New date:2007/12/9,第7章输入/输出流对象,2022/12/19,44,文件的输入/输出,如何对文本文件和二进制文件进行输入和输出操作?,第7章输入/输出流对象,2022/12/19,45,文件的概念,文件一般指的是磁盘文件,它是存储在磁盘上的相关数据集合。每个文件都有确定的名字。需要长久保存并能够被重新读写的信息,将它保存在外存文件上。C+常用的文件有程序文件,数据文件以及设备文件。键盘作为标准输入文件,显示器作为标准输出文件。C+标准库提供了相关的文件操作。对需要输入的大批量数据,可以事先以文件的形式存放在磁盘上,在程序中,从指定的文件中读取数据;程序的运行结果也可以写入磁盘上指定的文件,使用时再将文件中的数据读入。,第7章输入/输出流对象,2022/12/19,46,C+把文件看作是一个字符(字节)的序列,即由一个个字符(字节)数据顺序组成。文件按存储格式分二类:一种为字符格式文件,简称字符文件(ASCII码文件或文本文件),另一种为内部格式文件,简称字节文件(二进制文件)字符文件中,每一个字节单元的内容存放一个字符的ASCII码,能够直接被显示和打印。字节文件中,是把内存中的数据按其在内存中的存储形式原样输出到磁盘文件存放。,第7章输入/输出流对象,2022/12/19,47,两种文件的比较,ASCII码文件占用字节多,把内存中的数据写入ASCII码文件或从ASCII码文件读数据存放在内存中,需要转换。二进制文件占用字节少,把内存中的数据写入二进制文件或从二进制文件读数据存放在内存中,不需要转换。例如:10000是整型数据,用二进制表示占两或四个字节,如用ASCII码表示,则占五个字节。对于字符信息,在字符文件和字节文件中保存的信息是相同的。对于数值信息,在字符文件和字节文件中保存的信息是不同的。,第7章输入/输出流对象,2022/12/19,48,文件操作,要在程序中使用文件,首先应在文件开始包含头文件:#include 在访问文件之前,用头文件fstream中提供的输入文件流类ifstream、输出文件流类ofstream或输入输出文件流类fstream定义一个文件流类的对象,然后用该对象调用相应类中的open成员函数,按照一定的打开方式打开一个文件。文件被打开后,就可以通过文件流对象访问它。文件访问结束后,再通过流对象关闭它,释放缓冲区。,第7章输入/输出流对象,2022/12/19,49,文件操作的一般步骤,为文件定义一个流类对象;使用open()函数建立(或打开)文件。如果文件不存在,则建立该文件;如果磁盘上已存在该文件,则打开该文件;进行读写操作。在建立(或打开)的文件上执行所要求的输入/输出操作。一般来说,在内存与外设的数据传输中,由内存到外设称为输出或写,反之则称为输入或读;使用close()函数关闭文件。当完成操作后,应把打开的文件关闭,避免误操作。,第7章输入/输出流对象,2022/12/19,50,文件的打开,每个文件流类都有一个open成员函数,并且具有完全相同的声明格式,具体声明格式为:void open(const char * fname, int mode, int access);其中,fname指向要打开文件的文件名字符串,mode指定打开文件的方式,对应的实参是ios类中定义的open_mode枚举类型中的枚举常量,或由这些枚举常量构成的按位或表达式,access指定文件的访问方式。在C+中,打开一个文件就是将这个文件与对应流对象建立关联;,第7章输入/输出流对象,2022/12/19,51,第7章输入/输出流对象,2022/12/19,52,定义文件流对象和打开文件举例:1)ofstream fout;fout.open(“a:data1.dat”);2)ifstream fin; fin.open(“a:data1.dat”,ios:in|ios:nocreat);3)ofstream ofs; ofs.open(“a:data1.dat”, ios:app);4)fstream fio; fio.open(“a:data1.dat”,ios:out|ios:binary);,第7章输入/输出流对象,2022/12/19,53,文件的关闭,每个文件流类中都提供有一个关闭文件的成员函数close,当打开的文件操作结束后,就需要关闭它,使文件流与对应的物理文件断开联系。关闭任何一个流对象所对应的文件,就是用这个流对象调用close成员函数。文件流对应的文件被关闭后,还可以利用该文件流调用open成员函数打开其他的文件。ofstream fout;fout.open(“a:data1.dat”);fout.close();,第7章输入/输出流对象,2022/12/19,54,文件指针对于每个打开的文件,都存在着一个文件指针,初始指向一个隐含的位置,该位置由具体打开方式决定。对文件的读写都是从当前文件指针所指的位置开始,在读写过程中,文件指针顺序后移。文件的结束标志每个文件都有一个结束标志。当文件指针移到文件的结束标志处时,表示文件结束。,第7章输入/输出流对象,2022/12/19,55,测试文件是否结束,常用的方法有两种:1.ASCII码文件的结束标志用“-1”表示。C+在ios类中用符号常量“EOF”表示-1,当读取到的字符等于文件结束符EOF时表示文件访问结束。2.C+还提供了使用流对象调用eof()成员函数来测试文件当前状态是否“文件结束”。如果eof()的值是非0值,表示文件结束;如果eof()的值是0,则表示文件没有结束。用该方法测试的文件既可以是ASCII码文件又可以是二进制文件。,第7章输入/输出流对象,2022/12/19,56,字符文件的访问操作,向字符文件输出数据指依次把数据写入到文件的末尾(文件结束符也随之后移,它始终占据整个文件空间的最后一个字节位置)。从字符文件输入数据指从文件开始位置起依次向后提取数据,直到碰到文件结束符为止。,第7章输入/输出流对象,2022/12/19,57,字符文件的访问操作,向字符文件输出数据调用从ostream流类中继承来的插入操作符重载函数。调用从ostream流类中继承来的put成员函数。声明格式如下:ostream,第7章输入/输出流对象,2022/12/19,58,字符文件的访问操作,从字符文件输入数据调用提取操作符重载成员函数,每次从文件流中提取用空白符隔开的一个数据。调用get成员函数,每次从文件流中提取一个字符并作为返回值返回。调用getline成员函数,每次从文件流中提取一行字符到字符指针所指向的存储空间中。可以在istream文件中找到上述成员函数声明。,第7章输入/输出流对象,2022/12/19,59,例 字符文件的读写#include #include using namespace std;int main() char s18,s28;fstream outfile;cins1;outfile.open(“exam.txt”,ios:out);if(!outfile)couts2;couts2endl;outfile.close();return 0;,运行结果:abcdabcd,第7章输入/输出流对象,2022/12/19,60,字节文件的访问操作,字节文件是指在打开方式中带有ios:binary选项的文件。向字节文件输出数据把内存中由指定字符指针所指向的具有一定字节数的内容原原本本地写入到文件中,文件指针后移。从字节文件输入数据把具有一定字节数的内容原原本本地拷贝到内存中由指定字符指针所指向的存储空间中,文件指针后移。,第7章输入/输出流对象,2022/12/19,61,字节文件的访问操作,通过文件流对象调用在istream流类中定义的read成员函数能够从文件流对象所对应的文件中读出信息。通过文件流对象调用在ostream流类中定义的write成员函数能够向文件流对象所对应的文件中写入信息。这两个成员函数的声明格式如下:istream,第7章输入/输出流对象,2022/12/19,62,例 字节文件的读写#include #include using namespace std;struct personchar name20;double height;unsigned short age;people4=“wang”,1.65,25,“zhang”,1.78,24,“li”,1.85,21,int main() fstream infile, outfile;outfile.open(“exam.dat”,ios:out|ios:binary);if(!outfile)cout“exam.dat cant open”endl;abort();,第7章输入/输出流对象,2022/12/19,63,for(int i=0;i4;i+)outfile.write(char*),运行结果:wang1.6525zhang1.7824Li1.852100,第7章输入/输出流对象,2022/12/19,64,文件的随机读写,C+文件都是流文件,随机文件的读写与文件指针相关,文件指针记录着文件流的当前位置。有3个成员函数能对文件指针进行操作:istream 其中,streampos被定义为long型量, 都是long型量,并以字节为单位。具有如下含义:ios:cur,相对于当前读指针所指定的位置;ios:beg,相对于流的开始位置;ios:end,相对于流的结尾处;,第7章输入/输出流对象,2022/12/19,65,例 文件的随机读写#include #include using namespace std;int main() fstream file (“exam.dat”,ios:in|ios:out|ios:binary);if(!file)cout“exam.dat cant open”endl;abort();for(int i=0;i15;i+)file.write(char*),第7章输入/输出流对象,2022/12/19,66,file.seekg(pos);file.read(char*)seekg就是seek get,seekp是seek put,前者用于ifstream对象,后者用于ofstream对象,但是在ftream对象里,两者没有区别。,运行结果:current byte number:60the data stored is 15the data stored is 95the data stored is 21current byte number:88,第7章输入/输出流对象,2022/12/19,67,下面程序用istream和ostream类的操作符对点数据进行文件操作,从键盘上输入两个点的坐标,把它保存到磁盘文件上,并从磁盘上读出数据,输出到显示器上。#include #include using namesoace std;class Pointpublic:Point(int x,int y) X = x; Y = y;friend ostream,第7章输入/输出流对象,2022/12/19,68,ostream,第7章输入/输出流对象,2022/12/19,69,coutxy;Point Point1(x,y);coutxy;Point Point2(x,y);writefilePoint1Point2;coutPoint1endl;coutPoint2endl;return 0;,第7章输入/输出流对象,2022/12/19,70,字符串流,C+支持内存中的输入输出,只要将流对象与存储在内存中的char*或string捆绑起来即可。针对char*和string两种类型的对象,C+提供了两种字符串流,一种在中定义,另一种在中定义,两者功能基本相同。包含istrstream,ostrstream和strstream,是基于C风格字符串char*编写的。包含istringstream,ostringstream和stringstream等类,是基于C+中std:string编写的。,第7章输入/输出流对象,2022/12/19,71,字符串流,一个字符串流被定义后就可以调用相应的成员函数进行数据的输入、输出操作。对字符串流的操作方法通常与对字符文件流的操作方法相同。字符串流对应的访问空间是内存中由用户定义的字符数组,对字符串流的操作实质上就是在该数组上进行的。与文件流不同,字符串流对应的字符数组中没有相应的结束符标志,用户可以规定一个特殊字符作为其结束符使用。,第7章输入/输出流对象,2022/12/19,72,例 字符串流对象的读写#include #include #include using namespace std; int main() stringstream ostr(ccc); ostr.put(d); ostr.put(e); ostra; coutaendl;return 0;,运行结果:defgd,第7章输入/输出流对象,2022/12/19,73,例 字符串流对象用于类型转换#include #include #include using namespace std; int main() stringstream sstr; int a=100; string str; sstrstr; coutcname; coutcnameendl;return 0;,运行结果:100colinguan,第7章输入/输出流对象,2022/12/19,74,小结,C+标准库提供了一组类簇来处理输入输出:iostream类处理面向流的输入输出;fstream类处理面向文件的输入输出;stringstream类处理内存中字符串的输入输出;所有这些类都是通过继承相互关联的,输入类继承了istream,输出类继承了ostream,输入输出类继承了iostream,可在基类对象上执行的操作同样适用于派生类对象。,