c++学习笔记(十)

十九、多态性和虚函数
1、多态性:(静态多态性和动态多态性)
多态性是面向对象编程的重要特征,意思是一个事物有多种形态,表现为向不同的对象发送同一个信息,不同的对象在接受时会产生不同的行为。类似函数的重载,运算符的重载。
多态性的表现形式之一是具有不同功能的函数可以用同一个函数名,这样就可以实现用一个函数名调用不同内容的函数。
静态多态性又称为变成是的多态性,静态多态性的函数调用速度快,效率高,不灵活;动态多态性又称为运行时的多态性,是通过虚函数实现的。
2、利用虚函数实现动态多态性:
|、虚函数的作用:
同一类中不能定义两个名字相同,参数个数相同和类型相同的函数,但在类的继承层次结构中,在不同的层次可以出现名字参数个数和类型都相同而功能不同的函数,因为不在同一个类中,按照同名覆盖的原则决定调用的对象,区分是需要加上类名::。所谓虚函数就是在基类声明函数是虚拟的,并不是实际存在的函数,在派生类中才正式定义次函数,在程序运行期间用指针指向某一派生类对象,就像上车后再告知目的地。
使用方法是:
(1)在基类中用virtual声明成员函数为虚函数,在类外定义虚函数时不必再加virtual。
(2)在派生类中重新定义次函数,函数名,函数类型,函数参数个数和类型都相同,然后再根据效果重新定义函数体,当一个成员函数被声明为虚函数时,其派生类中的同名函数都自动成为虚函数,因此在派生类重新声明该虚函数时不需要加virtual,但习惯上一般在每一层声明该函数时都加上。如果在派生类中没有对基类的虚函数重新定义,那么简单地继承其直接基类的虚函数。
(3)定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的的对象。
(4)通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。
||、什么情况下应当声明虚函数。
使用虚函数的时候要注意:
(1)只能用virtual声明类的成员函数,把它作为虚函数,而不能将类外的普通函数声明为虚函数,因为虚函数的作用是允许在派生类中对基类的虚函数重新定义,只适用于类的继承层次中。
(2)一个成员函数被声明为虚函数后,在同一类族中就不能再定义一个非virtual当与该虚函数具有相同的参数(包括个数和类型)和函数返回值类型的同名函数。
适用于声明虚函数的情况是:
(1)首先看成员函数所在的类是否为基类。然后看成员函数在类的继承后有无可能改变功能。
(2)应考虑对成员函数的调用是通过对象名还是通过基类指针或引用去访问。
(3)有时,在定义一个虚函数时,并不定义函数体,即函数体是空的,只是定义了一个虚函数名,具体功能留给派生类去添加。
3、纯虚函数与抽象类:
|、纯虚函数:
纯虚函数是在声明虚函数时被初始化为0的函数,一般形式为:
virtual 函数类型 函数名 (参数列表)=0;
纯虚函数没有函数体,最后面的=0并不是函数的返回值是0,只起形式上的作用,这是一个声明语句,后面应该有分号。
纯虚函数中有函数的名字,不能被调用,留在派生类中定义。纯虚函数的作用是在积累中卫派生类保留一个函数的名字,以便调用,实现多态性。如果一个基类中声明了纯虚函数,在派生类中没有给出定义,那么在派生类中仍为纯虚函数。
||、抽象类:
定义一些不用来生成对象的类用来做为基类去建立派生类,作为一种基本类型提供给用户。这种不用来定义对象只用来最为一种基本类型来继承的类称为抽象类。凡是包含纯虚函数的类都是抽象类,因为抽象函数都不能被调用。

二十、输入输出流
1、c++的输入输出流:
|、输入输出流定义:
程序的输入是指从输入文件将数据传送给程序,程序的输出是指从程序将数据传送给输出文件,包括:
!、对系统制定的标准设备的输入输出。称为标准的输入输出,即I/O
!!、以外存(磁盘,光盘)为对象进行输入输出。
!!!、对内存指定空间进行输入输出。
||、类型安全和可拓展性:
在c++的输入输出中会对数据类型进行严格地检查,凡是类型不正确的数据不可能通过编译,因此是类型安全的。并且可以通过修改和补充,是用户自己声明类型对象的输入输出,即为可拓展性。c语言用scanf和printf实现输入输出,c++用cin和cout来实现输入输出。
|||、c++的输入输出流:
c++将输入输出的过程称为流。流的内容可以是ASCII字符,二进制形式的数据,图形图像,数字音频视频或其他形式的信息。内存中会为这些数据开辟一个缓冲区,在缓冲区里的数据就是流。
在c++中,输入输出流被定义为类,c++的I/O库中的类称为流类,用流类定义的对象称为流对象,像cout和cin,他们是iostream类中的对象。
!、c++的流库:
c++提供了一些类,专门用来输入输出,组成一个流类库,有两个基类:ios类和streambuf类,所有的流类都是从他们直接或间接派生出来的。ios是输入输出流,ios类是输入输出操作在用户端的接口,为用户的输入输出提供服务,streambuf是处理流缓冲区的类,包括缓冲区起始地址,读写指针和对缓冲区的读写操作。
在这里插入图片描述
ios是抽象基类,由它派生出istream类和ostream类,i和o分别代表input和output,iostream是从istream类和ostream类通过多重继承而派生的类。
c++对文件的输入输出需要用到ifstream类和ofstream类,f代表文件file,ifstream继承了类istream,ofstream类继承了类ostream,fstream类继承了类iostream
!!、与流类库有关的头文件:
iostream包含了对输入输出流进行操作的基本信息
fstream包含了用于用户管理的文件的I/O操作
strstream用于字符串流
stdiostream用于混合使用C和C++的I/O机制
iomanip用于使用格式化I/O
!!!、在iostream头文件中定义的流对象:
在iostream头文件中声明的类有ios,istream,ostream,iostream,istream_withassign,ostream_withassign,iostream_withassign等。
cin是istream的派生类istream_withassign的对象,是从标准输入设备(键盘)输入到内存的数据流,称为cin流或标准输入流。cout是ostream的派生类ostream_withassign的对象,他是从内存输入到标准输出设备(显示器)的数据流,称为cout流或标准输出流。
cerr和clog类似,均为向输出设备(显示器)输出出错信息。
!V、在iostream头文件中重载运算符:
“<<”和“>>”本来是c++定义为左位移运算符和右位移运算符的,由于系统在iostream中对他们进行了重载,是他们能做标准类型数据的输入输出运算符,在使用的时候一定要有iostream头文件。
2、标准输出流:
标准输出流就是流向标准输出设备(显示器)的数据。
|、cout,cerr和clog流:
!、cout流对象:
cout是console output的缩写,意为在控制台(终端显示器)的输出。
(1)cout不是c++预定义的关键字,是ostream流派生类的对象。
(2)cout输出时不用考虑数据是什么类型。
(3)cout流在内存中对应开辟了一个缓冲区,用来存放流中的数据,当插入一个endl时,不论缓冲区是否已满,都立即输出流中的所有数据,然后插入一个换行符,并刷新流(清空缓冲区)。
!!、cerr流对象:
cerr流对象是标准错误流,cerr流已被指定为与显示器关联,作用是向标准错误设备输出有关出错信息,是console error的缩写,以为在控制台(显示器)显示出错信息。与cout的作用与用法差不多,其中信息是用户指定的。也是cerr<<“信息”<<endl;
!!!、clog流对象:
也是标准错误流,是console log的缩写,与cerr用法相同,只不过cerr不经过缓冲区。
||、标准类型数据的格式输出:
!、使用控制符控制输出格式:
利用iomanip中的控制符控制。
!!、用流对象的成员函数控制输出格式:
在这里插入图片描述
setf和setiosflags括号中的参数表示格式状态,格式状态在类ios中被定义为枚举值,因此在应用这些格式标志时要加上ios和域运算符。
在这里插入图片描述
注意如果是cout.用setf(),如果是“<<”过程中用setiosflags().
!!!、用流成员函数put输出字符:
ostream类提供了专门用来输出单个字符的成员函数put,一般形式为:cout.put(‘字符’);,可以是字符或ASCII码。可以在一个语句中连续调用put函数:cout.put().put().put();
还可以用putchar函数输出一个字符。直接使用即可:putchar(‘字符’);
3、标准输入流:
是从标准输入设备(键盘)流向程序的数据。
|、cin流:
是istream类的派生类的对象,通过流提取运算符“>>”从流中提取数据。通常会跳过输入流中的空格,tad键,换行符等空白字符。
在遇到无效字符或文件结束符是,输入流出于出错状态,即无法正常提取数据,此时对cin的所有提取操作将终止。一般以Ctrl+Z或Ctrl+D表示文件姐舒服。当cin出于出错状态时,如果测试cin的值,发现它的值为false(0)
||、用于字符输入的流成员函数:
!、用get函数读入一个字符:
(1)无参数的get函数:
调用形式为cin.get(),表示从指定的输入流中提取一个字符(包括空白字符),函数的返回值就是读入的字符,遇到文件结束符则返回EOF(-1)。
(2)有一个参数的get函数:
调用形式为cin.get(ch),表示从输入流中读取一个字符,赋给字符变量ch,如果读取成功函数返回非0值(真)否则返回0(假)
(3)有3个参数的get函数:
调用形式为cin.get(字符数组,字符个数n,终止字符)或cin.get(字符指针,字符个数n,终止字符),其作用是从输入流中读取n-1个字符,赋给指定的字符数组,读取成功返回非0值,否则返回0;
!!、用成员函数getline函数读入一段字符:
cin.getline(字符数组(或字符指针),字符个数n,终止标志字符);
|||、istream类的其他成员函数:
!、eof函数:
是end of file的缩写,表示文件结束。如果到达文件末尾,函数值为非零值,否则为0.
!!、peek函数:
是观察的意思,作用是观测下一个字符,调用形式为c=cin.peek();
!!!、putback函数:
一般调用为cin.putback(ch),作用是将前面用get或getline函数从输入流中读取的字符ch返回到输入流中,插入到当前指针位置。
!V、ignore函数:
一般形式为cin.ignore(n,终止字符)作用是跳过输入流中的n个字符,或在遇到指定的终止字符时提前结束。也可以不带参数或只带一个参数。
4、对数据文件的操作与文件流:
|、文件:
文件一般指存储在外部介质上数据的集合。一批数据是以文件的形式存放在外部介质(磁盘,光盘,U盘)。操作系统是以文件为单位对数据进行管理的。即如果想找存在在外部介质上的数据,选文件名找到指定文件,再从文件中读取。
文件分为:程序文件(源程文件<.cpp>,目标文件<.obj>,可执行文件<.exe>等)和数据文件(程序中的输入和输出的对象)。根据文件中的数据的组织形式可分为ASCII文件(文本文件text或字符文件)和二进制文件。
||、文件流类与文件流对象:
cin,cout只能处理以标准设备为对象的输入输出,而不能处理以磁盘文件为对象的输入输出,必须另外定义以磁盘文件为对象的输入输出流对象。
文件流是指以外存文件为输入输出对象的数据流。文件流本身不是文件,而是以文件为输入输出对象的流。c++的I/O库中定义了六种文件类可以用来对磁盘文件进行输入输出操作:istream(入),ostream(出),iostream(入&出),ifstream(入),ofstream(出),fstream(入&出)。
要以磁盘文件为对象进行输入输出,必须定义一个文件流类的对象,通过文件流类对象将数据从内存输出到磁盘文件,或者通过文件流对象从磁盘文件将数据输入到内存。
由于cin和cout已在iostram头文件中事先定义,不需要自己定义,而是哟好难过磁盘文件时,必须自己定义对象。此外,对磁盘文件的操作是通过文件流对象(而不是cin,cout)实现的,文件流对象是用文件流类定义的,而不用istream和ostream类来定义。
|||、文件的打开和关闭:需要加上头文件fstream,此时可以不写iostream
!、打开磁盘文件:
准备工作包括:
(1)为文件流对象和指定的磁盘文件建立关联,使文件流流向指定的磁盘文件。
(2)指定文件的工作方式,像是作为输入文件还是输出文件,ASCII文件还是二进制文件。
可以通过两种方法实现:
(1)调用文件流的成员函数open:
一般形式为:文件流对象.open(磁盘文件名,输入输出方式);例:
ofstream outfile;//定义ofstream类(输出文件流类)对象outfile
outfile.open(“f1.dat”,ios::out);//是文件流与f1.dat文件建立联系
第一行是准备工作,定义一个文件流类的对象,第二行是调用输出文件流的成员函数open打开磁盘文件f1.dat,并制定他为输出文件,文件流对象outfile将向磁盘文件f1.dat输出数据。ios::out是I/O模式的一种,表示以输出方式打开一个文件。
磁盘文件名可以包括路径,如“c:\new\f1.dat”如果缺少路径,则默认为当前目录下的文件。
(2)在定义文件流对象时指定参数:
在声明文件流类时定义类带参数的构造函数,其中包含了打开磁盘文件的功能,因此可以在定义文件流对象时指定参数,调用文件流类的构造函数来实现打开文件的功能。如:ofstream outfile(“f1.dat”,ios::out)。一般形式为:
ofstream 文件流对象(磁盘文件名,输入输出方式)
输入输出方式是在ios类中定义的,是枚举常量。每一个打开的文件都有一个文件指针,该指针的初始位置有I/O的方式决定,每次读写都从文件指针的当前位置开始,每读入一个字节,指针酒后一亿个字节,当文件指针移到最后,就会遇到文件结束符,此时流对象成员函数eof为非零值。
在这里插入图片描述
可以用位或运算符“|”对输入输出方式进行组合,但不能组合相互排斥的方式,如果打开失败,open函数的返回值为0。
注:数据是依次读入的,需要注意输入输出时文件中数据的顺序。向文件输入的时候要用空格或换行符将每个数据隔开,从文件输出的时候不需要。
!!、关闭磁盘文件:
使用文件流对象的close成员函数,实际上就是解除该磁盘文件与文件流的关联,原来设置的工作方式也失效,即open函数是建立文件流对象与某磁盘文件的关系,close是取消磁盘文件与文件流对象的关系。
#include
using namespace std;
int main(){
ofstream op;//定义输出文件流对象。
char aa[50];//文件名,一定是字符数组,不能字符串
cin>>aa;
op.open(aa,ios::app);//初始化op,建立联系
int a,b,c;
cin>>a>>b>>c;//输入数据
op<<a<<" “<<b<<” “<<c;//向文件输出数据
op.close();//解除联系
ifstream ip;//定义输入文件流对象
cin>>aa;
ip.open(aa,ios::in);//初始化ip,建立联系
int e,f,g;
ip>>e>>f>>g;//将文件中的数据输入给e,f,g
cout<<a<<” “<<b<<” “<<c<<endl;
cout<<e<<” “<<f<<” “<<g<<endl;
ip.close();//解除联系
}
|V、对ASCII文件的操作:
如果文件的每一个字节中均以ASCII代码形式存放数据,即一个字节存放一个字符,这个文件就是ASCII文件(字符文件)。如存放一篇英文文章的文本文件就是ASCII文件。对ASCII文件的读写有两种方法
!、用流插入运算符“<<”和流提取运算符“>>”输入输出标准类型的输入输出标准类型的数据。“<<”和“>>”都已在iostrea中重载为能用于ostream和istream类对象的标准类型的输入输出,由于ifstream和ofstream分别是ostream和istream的派生类,因此它们从ostream和istream类继承了公用的重载函数,可以用“<<”和“>>”实现对磁盘文件的读写,就如同用cin和cout实现对标准设备的读写。
!!、用put,get和getline函数进行输入输出。
V、对二进制文件的操作:
二进制文件不是以ASCII代码形式存放数据,它将内存中的护具存储形式不加转换地传送到磁盘文件,因此又称为内存数据的映像文件。文件中的信息不是字符数据,而是字节中的二进制形式的信息,又称为字节文件。
再打开时要用ios::binary指定为一二进制形式传送和存储。二进制文件除了可以作为输入文件或输出文件,还可以既能输入又能输出的文件。
!、用成员函数read和write读写二进制文件:
istream& read(char * buffer int len);
ostream& write(const char *buffer,int len);
字符指针buffer指向内存中一段存储空间,len是读写的字节数,调用的方式为a.write(p1,20),b.read(p2,20)。
a是输出文件流对象,write函数将字符指针p1指向的单元开始的20个字节的内容不加转换地写道与a关联的磁盘文件中,b是输出文件流对象,read函数从b关联的磁盘文件中读入20个字节(或遇到EOF结束),存放在字符指针p2所指的一段空间内。
#include
#include
using namespace std;
struct student{
char name[20];
int num;
int age;
char sex;
};
int main(){
student stu[2]={“Li”,1001,18,‘f’,“Fun”,1002,19,‘m’};
ofstream out(“stu.dat”,ios::binary);
if(!out){
cerr<<“open error!”<<endl;
abort();//退出程序
}
for(int i=0;i<2;i++)
out.write((char )&stu[i],sizeof(stu[i]));
out.close();
student stud[2];
int i;
ifstream in(“stu.dat”,ios::binary);
if(!in){
cerr<<“open error!”<<endl;
abort();//退出程序
}
for(i=0;i<3;i++)
in.read((char
)&stud[i],sizeof(stud[i]));
in.close();
for(i=0;i<2;i++){
cout<<“NO.”<<i+1<<endl;
cout<<“name:”<<stud[i].name<<endl;
cout<<“num:”<<stud[i].num<<endl;;
cout<<“age:”<<stud[i].age<<endl;
cout<<“sex:”<<stud[i].sex<<endl<<endl;
}
return 0;
}
!!、与文件指针有关的流成员函数:
在磁盘文件中有一个文件读写位置标记来指明当前应进行读写的位置。对于二进制文件允许对位置标记进行控制,移动到指定位置读写。
在这里插入图片描述
参照位置:ios::beg文件开头(默认值)ios::cur位置标记当前的位置ios::end文件末尾。
!!!、随机访问二进制数据文件:
可以将文件的工作方式指定为输入输出文件,正确计算好每次访问时指针的定位,即正确使用seekg或seekp函数,并对文件数据进行更新。
V|、字符串流:
文件流是以外存文件为输入输出对象的数据流,字符串流不是以外存文件为输入输出的对象,而是以内存中用户定义的字符数组(字符串)为输入输出的对象,即将数据输出到内存中的字符数组。
字符串流有相应的缓冲区,字符串流类有istrstream,ostrstream和strstream,都是ostream,istream,iostream类的派生类
输出时数据不是流向外存文件而是流向内存中的一个存储空间,输入时从内存中的存储空间读取数据。
字符串流对象关联的不是文件而是内存中的一个字符数组,因此不需要打开和关闭文件。每个文件最后都有一个文件结束符表示文件结束,而字符串流所关联的字符数组没有相应的结束标志,用户需要自己定义一个特殊字符作为结束符。
字符串流类没有open成员函数,因此要在建立字符串流对象时通过给定参数来确定字符串流与字符数组的关联,即通过调用构造函数来解决问题。
!、建立输出字符串流对象:
ostrstream::ostrstream(char *buffer,int n,int mode=ios::out);
可以用下面的语句建立输出字符串流对象并与字符数组建立关联:
ostrstream 对象名(字符数组名,流缓冲区大小);
buffer是指向字符数组首元素的指针,n为制定的流缓冲区的大小,参数3是可选的,默认为ios::out
!!、建立输入字符串流对象:
istrstream::istrstream(char *buffer);
istrstream::istrstream(char *buffer,int n);
可以用下面的语句建立输出字符串流对象并与字符数组建立关联:
istrstream 对象名(字符数组名);
istrstream 对象名(字符数组名,流缓冲区大小);
buffer是指向字符数组首元素的指针,用来初始化流对象(使流对象与字符数组建立关联)
!!!、建立输入输出字符串流对象:
strstream::strstream(char *buffer,int n,int mode);
可以用下面的语句建立输出字符串流对象并与字符数组建立关联:
strstream 对象名(字符数组名,sizeof(字符数组名),ios::in|ios::out);
建立输入输出字符串流对象,以字符数组buffer为对象。
以上都要用到头文件strstream。
#include
#include
using namespace std;
int main()
{
char c[50]=“12 34 65 -23 -32 33 61 99 321 32”;
int a[10],i,j,t;
cout<<“array c:”<<c<<endl;//显示字符数组中的字符串
istrstream strin(c,sizeof©);//建立输入串流对象并于字符数组关联
for(i=0;i<10;i++)
strin>>a[i];//从字符数组读入10个元素赋给整型数组a
cout<<“array a:”;
for(i=0;i<10;i++)
cout<<a[i]<<” “;//显示整形数组a
cout<<endl;
for(i=0;i<9;i++)//冒泡排序处理a
for(j=0;j<9-i;j++)
if(a[j]>a[j+1]){
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
ostrstream strout(c,sizeof©);//建立输出串流对象并于字符数组关联
for(i=0;i<10;i++)
strout<<a[i]<<” ";//将10个整数存放在字符数组中
strout<<ends;//加入’\0’
cout<<“array c:”<<c<<endl;//显示字符数组
return 0;
}
用字符串流时不需要打开和关闭文件(open和close成员函数),通过字符串流从字符数组读数据就如同从键盘读数据一样。
用输入字符串流向字符数组写数据时,由于是ios::out,更新了数组的内容。如果换成app可以连续写入。
字符串流关联的字符数组并不一定是专为字符串流而定义的数组,它与一般的字符数组无异,可对该数组进行各种操作。
虽然方便很多,但字符数组是有生命周期的,和主函数相同,只能作为临时的存储空间(因为没有生成文件,还是相当于在内存中)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值