面向对象程序设计(C++)课件 第3章 多态与友元_第1页
面向对象程序设计(C++)课件 第3章 多态与友元_第2页
面向对象程序设计(C++)课件 第3章 多态与友元_第3页
面向对象程序设计(C++)课件 第3章 多态与友元_第4页
面向对象程序设计(C++)课件 第3章 多态与友元_第5页
已阅读5页,还剩34页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

大学计算机基础教学系列教材第三章面向对象概念--多态与友元2026年5月11日目录多态第3.1节友元第3.2节本章小结第3.3节01020304CONTENTS052学习目标理解多态的概念,能区分静态多态(函数重载)与动态多态。掌握虚函数的定义、使用规则,理解纯虚函数与抽象类的特性及应用场景。明确虚析构函数与纯虚析构函数的作用。

理解友元的意义,掌握友元函数、友元类及成员函数友元的定义与使用方法。多态013第3.1节多态

classBase{public:

voidshow(){cout<<"Baseshow()"<<endl;}};classDerived:publicBase{public:多态性是基于封装和继承的面向对象核心特性,核心为“一个接口,多种实现”,能让同一函数调用在不同场景有不同行为,实现接口统一与解耦,按发生时机可分为静态多态和动态多态。静态多态又称编译时多态,常体现为函数重载,行为在编译阶段确定。4多态voidshow(){//重写函数

cout<<"Derivedshow()"<<endl;}};intmain(){Derivedd;d.show();Base*bptr=&d;bptr->show();}Derivedshow()Baseshow()程序输出如下:5多态classBase{public:

virtualvoidshow(){

cout<<"Baseshow()"<<endl;}};classDerived:publicBase{public:

voidshow()override{cout<<"Derivedshow()"<<endl;}};动态多态,也被称作运行时多态,动态多态的行为是在运行阶段动态确定的。6intmain(){Base*bptr=newDerived();bptr->show();}Derivedshow()程序输出如下:虚函数virtual函数类型函数名(参数列表){

函数体}虚函数的多态性是通过基类指针或基类引用对派生类对象的调用来实现的。constdoublePI=3.1415926;classCircle//圆形类{

doubler;//表示圆的半径public:Circle(doublerad){r=rad;}//构造函数

doublePeri(){return2*r*PI;}//求圆的周长7虚函数属于基类的成员函数,其格式定义如下:虚函数virtualdoubleArea(){returnPI*r*r;}//求圆的面积

};classCylinder:publicCircle//圆柱体类{

doubleh;//表示圆柱体的高度public:Cylinder(doublerad,doubleheight):Circle(rad){h=height;}

doubleArea(){returnPeri()*h;}//B派生后仍为虚函数,求圆柱体的侧面积};classCone:publicCircle//表示圆锥体类{

doubleh;//表示圆锥体的高度public:Cone(doublerad,doubleheight):Circle(rad){h=height;}8虚函数doubleArea(){returnPI*r*sqrt(r*r+h*h);}};voidfun(Circle*pb)//通用函数,通过基类指针实现动态联编{cout<<pb->Area()<<endl;}//主函数通过创建Cylinder和Cone对象,并调用全局函数fun,利用基类指针的多态性,统一计算并输出了不同几何体的侧面积。intmain(){Cylindercy(3,5);//定义圆柱体的对象

Conecn(3,5);//定义圆锥体的对象cout<<"圆柱体的侧面积是:";

fun(&cy);//圆柱体对象的地址为基类指针赋值

cout<<"圆锥体的侧面积是:";fun(&cn);//圆锥体对象的地址为基类指针赋值

return0;}9虚函数程序输出如下:圆柱体的侧面积是:94.2478圆锥体的侧面积是:54.95541011类对象的内存布局非静态成员变量:按类中声明顺序依次存储,不随机排列。虚函数影响:若类含虚函数,对象内存会额外增加虚函数表指针(vfptr),指向存储虚函数地址的“虚函数表”,是动态多态的实现基础。内存区域内容占用字节说明起始位置虚函数表指针(vfptr)4指向虚函数表,实现多态偏移4字节num(int)4按声明顺序存储偏移8字节内存对齐单元4补齐至double的8字节对齐要求偏移12字节value(double)8对齐后存储对象总大小-16满足所有成员的对齐规则classMyClass{public:

intnum;

doublevalue;

virtualvoidtest();};12纯虚函数基类将某成员函数定义为虚函数,非自身需求,而是为派生类预留函数名,功能由派生类实现,即预留待实现接口,便于编写通用程序,这种无具体实现的虚函数称为纯虚函数,语法如下:virtual函数类型函数名(参数列表)=0;说明:纯虚函数没有函数体,仅是一个申明语句,句末带有分号。“=0”表明该虚函数并无具体实现,仅为一种形式,可在派生类中进行继承与改写。具备纯虚函数的基类无法定义对象,不过能够定义其指针或引用。13纯虚函数如下代码所示,在派生类中实现基类所定义的纯虚函数的具体功能。constdoublePI=3.1415926;classCircle//圆形类{

doubler;//表示圆的半径public:Circle(doublerad){r=rad;}//构造函数

doublePeri(){return2*r*PI;}//求圆的周长

virtualdoubleArea(){returnPI*r*r;}//求圆的面积

virtualdoubleVolume()=0;//纯虚函数,求对象的体积

};classCylinder:publicCircle//圆柱体类{

doubleh;//表示圆柱体的高度纯虚函数public:Cylinder(doublerad,doubleheight):Circle(rad){h=height;}

doubleArea(){returnPeri()*h;}//虚函数,求圆柱体的侧面积doubleVolume(){returnCircle::Area()*h;}};classCone:publicCircle//表示圆锥体类{

doubleh;//表示圆锥体的高度public:Cone(doublerad,doubleheight):Circle(rad){h=height;}

doubleArea(){returnPI*r*sqrt(r*r+h*h);}

doubleVolume(){returnCircle::Area()*h/3;}};voidfun(Circle*pb)//通用函数,通过基类指针实现动态联编{cout<<pb->Area()<<endl;}14纯虚函数intmain(){Cylindercy(3,5);//定义圆柱体的对象

Conecn(3,5);//定义圆锥体的对象

cout<<"圆柱体的侧面积和体积是:";fun(&cy);//圆柱体对象的地址为基类指针赋值

cout<<"圆锥体的侧面积和体积是:";fun(&cn);//圆锥体对象的地址为基类指针赋值

return0;}程序输出如下:圆柱体的侧面积和体积是:94.2478141.372圆锥体的侧面积和体积是:54.955447.123915抽象类含至少一个纯虚函数的类为抽象类,纯虚函数可在本类定义或从抽象基类继承重定义。纯虚函数无默认实现,抽象类不能实例化,派生类须覆盖所有纯虚函数(否则也为抽象类),实例化抽象类会触发编译错误,以此确保派生类实现必要接口。classAnimal{public:

virtualvoidspeak()=0;//纯虚函数(必须由派生类实现)};classFlyingAnimal:publicAnimal{public:

virtualvoidfly()=0;//新增纯虚函数};16抽象类classBird:publicFlyingAnimal{public:voidspeak()override{cout<<"Chirp!"<<endl;}

voidfly()override{cout<<"Flying..."<<endl;}};classInvalidBird:publicFlyingAnimal{public:

voidspeak()override{cout<<"Chirp!"<<endl;}//未覆盖fly(),InvalidBird仍是抽象类};intmain(){Birdbird;bird.speak();//输出“Chirp!”bird.fly();//输出"Flying..."return0;}17抽象类抽象类仅能作为基类派生其他类,不可直接实例化;可定义其指针或引用,实现运行时多态。如下抽象类Animal无法实例化,但可通过指针/引用操作派生类对象。voidprintAction(Animal&animal){//通过基类引用实现多态

animal.speak();}

intmain(){//Animalanimal;//错误:抽象类不能实例化

Birdbird;Animal*ptr=&bird;//基类指针指向派生类对象

ptr->speak();//输出"Chirp!"printAction(bird);//输出"Chirp!"

return0;}18抽象类抽象类不可作为函数参数类型、函数返回值类型或者显式转换类型。正确的使用方法如下,可以运用指针或引用:voidprocessAnimal(Animalanimal){}//编译错误

//错误示例:抽象类作为返回值类型

AnimalcreateAnimal(){//编译错误

returnAnimal();}intmain(){Animal*ptr=newBird();//Animala=*ptr;//错误:抽象类不能用于类型转换

return0;}voidprocessAnimal(Animal&animal){}//通过引用传递Animal*createAnimal(){returnnewBird();}//返回基类指针19抽象类抽象类无法用于创建对象,仅能为派生类提供接口规范。派生类必须对抽象类中的纯虚函数进行重载,否则它依然会被视为抽象类。classShape{public:

virtualdoublearea()=0;//纯虚函数};//正确派生类:RectangleclassRectangle:publicShape{

doublewidth,height;public:Rectangle(doublew,doubleh):width(w),height(h){}

doublearea()override{returnwidth*height;}//覆盖纯虚函数};20抽象类//错误派生类:未覆盖area()classInvalidShape:publicShape{};//编译错误:仍是抽象类,因为没有重载 area()函数intmain(){Rectanglerect(3,4);cout<<rect.area()<<endl;//InvalidShapeshape;//编译错误

return0;}21程序输出如下:12虚析构函数

classBase{public:

virtual~Base(){cout<<“Basequit”<<endl;

}};classDerived:publicBase{

public:~Derived(){cout<<“Derivedquit”<<endl;}};intmain(){Base*b=newDerived();

deleteb;return0;}程序输出如下:DerivedquitBasequit22虚析构函数即声明为虚函数的析构函数。当基类指针/引用指向派生类对象时,若基类析构函数非虚函数,销毁对象时仅调用基类析构函数,不调用派生类的,可能导致资源泄漏。因此,基类析构函数应声明为虚函数。纯虚析构函数classBase{public:

virtual~Base()=0;//纯虚析构函数

};

classDerived:publicBase{

public://派生类的析构函数~Derived()override{

cout<<"Deriveddestroyed"<<endl;}};Base::~Base(){

cout<<"Basedestroyed"<<endl;}intmain(){Derivedd;

return0;}程序输出如下:DeriveddestroyedBasedestroyed23纯虚析构函数是被申明为纯虚函数的析构函数。这表明基类无法被实例化,只能由派生类继承并实现。纯虚析构函数通常用于抽象基类,以确保派生类必须提供自身的析构函数。虚析构函数与纯虚析构函数虚析构函数可通过基类指针/引用删除派生类对象时调用其析构函数;纯虚析构函数用于抽象基类,强制派生类提供析构函数且基类无法实例化。实际编程中,抽象基类用纯虚析构函数,非抽象基类需确保派生类析构调用时用虚析构函数。特化虚析构函数纯虚析构函数能否实例化√×(抽象类)派生类对象析构函数的调用方式通过基类指针或引用删除派生类对象时,先调用派生类的析构函数,然后调用基类的析构函数。派生类必须提供自己的析构函数,确保在对象销毁时能够正确地调用派生类的析构函数。使用场景用于可能被直接实例化的类,确保派生类析构函数被调用。用于抽象基类,确保派生类必须提供自己的析构函数,基类不能被实例化。24友元0225第3.2节友元函数当一个类成员函数作为另一个类的友元函数时,需先定义友元函数所在的类。class

类名{…

friend

函数类型函数名(<参数列表>);...};classTri{

doublea,b,c;

public:Tri(doublex,doubley,doublez){a=x;b=y;c=z;}26类的友元函数的定义方法是将关键字friend置于函数原型之前。友元函数的定义语法如下:友元函数

friendvoidDisplay(Tri&t);};voidDisplay(Tri&t){cout<<"输出三角形三边:\n";cout<<t.a<<'\t'<<t.b<<'\t'<<t.c<<endl;}

intmain(){Trit1(3,4,5);Display(t1);

return0;}程序输出如下:输出三角形三边:3452728友元函数注意事项说明如下:友元函数非类成员函数,无this指针,需通过“对象名。成员名”访问类成员,形参通常为对象引用。友元函数在类内的声明位置(public/private/protected)不影响其访问权限,可自由声明。友元函数需谨慎使用,仅用于读取类成员、不修改成员数据的场景相对安全。29友元类类也可作为另一个类的友元(即友元类),友元类能自由访问该类的所有成员。例如:若类B是类A的友元类,需在类A中声明类B,声明形式遵循相应规范。对于类B而言,类A是其基类;类B必须借助类A的实例对象,方可访问和使用类A中的成员。classAclassB{…{

friendclassB;……};};友元类classVolume;//首先,申明类BclassCircle//圆形类,相当于类A{

doubler;

public:Circle(doublea){r=a;}

friendclassVolume;//类B是类A的友元类};classVolume//圆柱体类,相当于类B{

doubleh;public:Volume(doublehigh){h=high;}

voidCal(Circle&c)//自由引用类A中的私有数据成员

{30友元类

doublev=3.1415926*c.r*c.r*h;cout<<"半径为"<<c.r<<"的圆柱体的体积是:"<<v<<endl;}};intmain(){Circlecl(2),c2(3);Volumev(2);v.Cal(cl);v.Cal(c2);

return0;}程序输出如下:半径为2的圆柱体的体积是:25.1327半径为3的圆柱体的体积是:56.54873132成员函数做友元一个类的友元函数既可以是普通函数,也可以是另一个类的成员函数。换句话说,若将类B的某个成员函数申明为类A的友元函数,那么在这个成员函数中就能自由引用类A的所有成员。这相当于类A对象的所有成员对该函数是“开放”的。在类A中申明类B的成员函数为类A的友元函数,形式如下:classA{…

friend

函数类型B::成员函数名(<参数列表>);…};33成员函数做友元在编写程序时,若希望类B的某个成员函数能够直接访问类A的所有私有成员,也就是让类B的函数成为类A的“内部好友”,则必须按照特定的顺序进行操作:在代码最前面用一行classA;提前告诉编译器“类A存在”;完整定义类B,但类B中那些需要操作类A成员的函数,先申明,可暂时不写具体实现;正式定义类A,并在类A内部通过friend语法,明确将类B的指定函数设为友元;回到类B的定义之后,再

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论