版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第十四章继承与多态C++程序设计——大模型思维与实践内容导航继承大模型实践多态大模型探究纯虚函数与抽象类本章小结实践探究继承3在C++中,继承是一种面向对象的编程特性,它使得新创建的类(派生类或称子类)能够继承另一个类(基类或称父类)的属性和方法。不仅保留基类的所有特性,还允许派生类根据需要添加新的特性或重写基类中的方法。这种从基类到派生类的特性传递,被形象地称为“继承”。继承的概念4继承的概念多层继承单继承每个派生类都明确地从一个基类继承。派生类可以看作是基类的一个特殊版本,继承了基类的所有成员,并可添加新的成员或修改已有成员的行为。多(重)继承允许一个派生类同时从两个或以上基类继承,从而集成了多个基类的特性。多层继承一个派生类本身也可以作为另一个新派生类的基类,从而形成一个类的继承链。继承机制可减少代码冗余,提高代码的重用率,促进软件设计的模块化与抽象化。5语法格式class派生类名:继承方式1基类名1,继承方式2基类名2,…{private:
派生类的私有数据和函数
public:
派生类的公有数据和函数
protected:
派生类的保护数据和函数};注意:每一种继承方式只限定紧跟其后的那个基类。如果在基类名前没有显式指定继承方式,C++对class将默认采用私有继承,对struct默认采用公有继承。派生类的定义继承方式:public:表示公有继承;private:表示私有继承;protected:表示保护继承;类名后跟冒号基类名表,表示当前定义的派生类的一个或多个基类。实现单继承或多继承。6例:定义Teacher和Student类从Person派生。派生类的定义classStudent:publicPerson{private:stringstuNo;doublescore;public:voidsetStuNo(stringn){stuNo=n;}};intmain(){Teachert;t.setName("li");t.setEmployeeNo("104320");return0;}classPerson{private:stringname;chargender;intage;public:voidsetName(stringn){name=n;}};classTeacher:publicPerson{private:stringemployeeNo;doublesalary;public:voidsetEmployeeNo(stringn){employeeNo=n;}};基类派生类,公有继承派生类,公有继承通过派生类Teacher的对象t,可以访问基类成员函数setName继承实现代码重用classPerson{public:stringname;chargender;intage;};classTeacher:publicPerson{public:stringemployeeNo;doublesalary;};classStudent:publicPerson{public:stringstuNo;doublescore;};voidmain(){Teachert;="li";t.employeeNo="104320";}classPerson{…}classTeacher{public:stringname;chargender;intage;stringemployeeNo;doublesalary;};classStudent{public:stringname;chargender;intage;stringstuNo;doublescore;};voidmain(){Teachert;="li";t.employeeNo="104320";}可认为定义了从实现的角度来看,派生类对象内部确实有一个基类子对象,但从编程接口的角度来看,可以认为派生类“自己定义了”这些从基类继承的成员。提高了代码的可重用性和可维护性。8继承方式C++提供了三种基本的继承方式:公有继承(public)、保护继承(protected)和私有继承(private)。派生类从基类继承的成员,在派生类的作用域内,犹如派生类自行定义的成员一般。示例继承方式classBase{private:inta;protected:intb;public:intc;};classDerived:(继承方式)Base{…};classDerived{
不可见的inta;//该成员存在于基类子对象中,派生类无法直接访问//成员b和c的访问控制符受到基类成员原有访问控制符以及继承方式的共同约束
访问控制符1:intb;
访问控制符2:intc;};对于Derived类,相当于定义了为何值?9继承方式
继承方式
基类成员publicprotectedprivatepublicpublicprotectedprivateprotectedprotectedprotectedprivateprivate派生类中不可见派生类中不可见派生类中不可见基类成员被派生类继承后的访问权限(表现出的控制符)派生类的成员函数可访问外部函数(全局函数或其它类的成员函数)通过对象仅可访问该部分成员。在派生类中的访问权限取决于基类成员访问控制符和继承方式两者中的更严格限制10class
Person{private:intage;protected:chargender;public:stringname;};class
Teacher:public
Person{public:
voidsetValue(){age=30;gender='M';name=
"li";
}};继承方式:举例class
Student:protected
Person{public:voidsetValue(){age=30;gender='M’;name=
"li";}};class
Worker:private
Person{public:voidsetValue(){age=30;gender='M’;name=
"li";}};intmain(){Teachert;t.age=30;t.gender='M’;
=
"li";Students;s.age=30;s.gender='M’;
=
"li";Workerw;w.age=30;w.gender='M’;
=
"li";}访问权限不变都变成protected都变成private继承后继承后继承后基类的protected成员可以被派生类的成员函数访问,而private成员只能被自己的成员函数访问。11Teacher类从Person类保护继承:继承方式:保护继承与私有继承的区别classPerson{public:stringname;};classTeacher:protectedPerson{};classProfessor:publicTeacher{public: voidsetName(stringname){this->name=name;} voiddisplayName(){cout<<name<<endl;}};intmain(){ Professorp; p.setName("abc"); p.displayName();}classPerson{public:stringname;};classTeacher:privatePerson{};classProfessor:publicTeacher{public:voidsetName(stringname){this->name=name;}voiddisplayName(){cout<<name<<endl;}};改为私有继承继承方式protected和private的区别为:保护继承中,基类公有成员(及保护成员)在派生类中变为保护成员,派生类的派生类可访问;私有继承中,基类公有成员(及保护成员)在派生类中变为私有成员,派生类的派生类不可访问。12构造与析构介绍由于派生类继承了其基类的成员,所以在建立派生类的实例对象时,必须初始化从基类继承的数据成员(即完成基类子对象的构造)。派生类对象析构时,也必须析构基类子对象。派生类不直接继承基类的构造函数、析构函数,但是派生类能自动调用基类的构造函数、析构函数派生类构造函数在创建对象时,负责调用基类的构造函数完成基类子对象的初始化。当派生类对象的生命周期结束时,其析构函数将被调用以执行清理工作。在派生类析构函数中,基类的析构函数也会被自动调用,以确保基类子对象被正确销毁。派生类的构造和析构13基类构造函数的调用方式显式调用在派生类构造函数的初始化列表中,可以为基类构造函数提供参数,明确指定要调用的基类构造函数。这种方式允许根据需要在派生类构造过程中控制基类成员的初始化。隐式调用如果派生类构造函数无初始化列表或初始化列表未为基类构造函数提供参数,派生类构造函数将自动调用基类的无参构造函数来初始化基类子对象。如果基类没有无参构造函数,将编译出错。派生类的构造和析构14派生类构造函数的定义语法格式:派生类名(参数总表):基类名1(参数表1),...,基类名m(参数表m),
成员对象名1(成员对象参数表1),...,成员对象名n(成员对象参数表n){//派生类新增成员的初始化//这里包含的初始化代码块通常用于处理那些无法通过初始化列表初始化的成员}派生类的构造和析构基类成员的初始化表,指定被调用的基类构造函数成员的初始化表,初始化派生类中声明的任何成员classPoint{intX,Y;public:Point(intx,inty){X=x;Y=y;
cout<<"point("<<X<<","<<Y<<")constructing"<<endl;}~Point(){
cout<<"point("<<X<<","<<Y<<")destructing"<<endl;}};classColor{intR,G,B;public:Color(intr=0,intg=0,intb=0){R=r;G=g;B=b;cout<<"color("<<R<<","<<G<<","<<B<<")constructing"<<endl;}~Color(){cout<<"color("<<R<<","<<G<<","<<B<<")destructing"<<endl;}};15派生类的构造和析构【例14-4】定义Circle类,继承自Point类,并包含成员对象线条颜色lineColor和填充颜色fillColor,颜色由R、G、B三个整数值进行描述。(X,Y)radius16派生类的构造和析构classCircle:publicPoint{doubleradius;//半径ColorlineColor,fillColor;public:Circle(doubleR,intX,intY,intr,intg,intb):fillColor(r,g,b),Point(X,Y){ radius=R; cout<<"circleconstructing,radius:"<<R<<endl;} ~Circle(){cout<<"circledestructing,radius:"<<radius<<endl;}};intmain(){Circlecir(3,0,0,192,192,192);}运行结果:point(0,0)constructingcolor(0,0,0)constructingcolor(192,192,192)constructingcircleconstructing,radius:3circledestructing,radius:3color(192,192,192)destructingcolor(0,0,0)destructingpoint(0,0)destructing尽管在初始化表中,成员对象名fillColor排在基类名Point之前,构造时,仍然是先构造基类子对象,与这里的排列顺序无关为基类子对象的构造函数提供参数为成员对象的构造函数提供参数构造函数未获得参数在构造函数体中初始化17单继承的构造与析构顺序(1)调用基类构造函数(2)调用内嵌成员对象的构造函数(3)调用派生类自己的构造函数派生类的构造和析构析构函数的调用顺序遵循和构造函数完全相反的顺序。首先调用派生类析构函数(清理派生类新增成员);然后调用派生类成员对象析构函数(清理派生类新增的成员对象);最后调用基类析构函数(清理从基类继承来的基类子对象)。调用顺序按照它们在类中定义的顺序对照理解例14-4的构造与析构的顺序18说明:(1)如果基类本身还有基类(多层继承)基类的构造函数将按照继承链的顺序被递归调用,最先调用的是最顶层的基类构造函数,然后是次顶层基类的构造函数,依此类推,直到直接基类的构造函数被调用。(2)如果基类包含成员对象需要先构造出基类的成员对象,才执行基类自己的构造函数。(3)成员对象的类也有基类及成员对象同样需要先构造其基类和它的成员对象,最后才构造成员对象自己。派生类的构造和析构全部完成,才算完成基类的构造19【例14-5】基类与成员对象的构造均涉及到递归构造。classA{public:A(){cout<<'A';}~A(){cout<<"~A";}};classB:publicA{public:B(){cout<<'B';}~B(){cout<<"~B";}};classC{public:C(){cout<<'C';}~C(){cout<<"~C";}};classD{public:D(){cout<<'D';}~D(){cout<<"~D";}};classE:publicD{Cc;public:E(){cout<<'E';}~E(){cout<<"~E";}};classF:publicE{Bb;public:F(){cout<<'F';}~F(){cout<<"~F";}};intmain(){Ff;}运行结果DCEABF~F~B~A~E~C~D类的关系图派生类的构造和析构20何时需要定义派生类的构造函数当基类拥有无参构造函数时,派生类可以不定义构造函数情况分析:若基类定义了一个无参构造函数(无论是显式定义还是编译器自动生成的默认构造函数),则派生类在定义自己的构造函数时,可以隐式调用基类构造函数(即不需为基类构造函数提供参数)。特例:如果派生类本身也不需要额外的初始化操作,那么派生类可以不显式定义构造函数,此时编译器会生成一个默认的构造函数,该构造函数会隐式调用基类的无参构造函数。派生类的构造和析构21class
B{//以下无参构造函数可以自己定义,也可不定义,编译器会生成一个默认构造函数//B(){}};class
D:public
B{//基类拥有无参构造函数的情况下,派生类可以不定义构造函数//隐式调用基类的无参(默认)构造函数//D(){}};派生类的构造和析构举例:22何时需要定义派生类的构造函数当基类只定义了带参数的构造函数时,派生类必须定义构造函数情况分析:如果基类仅提供了带参数的构造函数,而没有无参构造函数,那么派生类在构造时必须显式地调用基类的某个构造函数,以确保基类子对象构造时能获得合适的参数。必要性:此时,派生类必须定义自己的构造函数,并在其初始化列表中明确指定要调用的基类构造函数及其参数。即使派生类本身没有需要初始化的成员变量,也必须定义构造函数。派生类的构造和析构23class
B{
stringname;public:B(string
n)//基类仅有带参数的构造函数{name=n;}};class
D:public
B{public://派生类必须定义构造函数,为基类的构造函数提供参数D(string
n):B(n){}};派生类的构造和析构举例:24何时需要定义派生类的构造函数派生类需要额外初始化时,需要定义自己的构造函数情况分析:无论基类构造函数如何定义,如果派生类拥有需要初始化的成员变量(例如数据成员为常量或引用),派生类都必须定义自己的构造函数,并在初始化列表中完成这些成员变量的初始化。综合考量:在这种情况下,即使基类提供了无参构造函数,派生类也可能需要定义自己的构造函数来处理派生类成员的初始化。派生类的构造和析构25class
B{
stringname;public:B(){name=
"";}B(string
n){name=
n;}};class
D:public
B{
const
intN;public:
//派生类构造函数需要初始化常量N,必须定义构造函数D(string
n):N(10){}//将自动调用基类的无参构造函数};派生类的构造和析构举例:26多继承时派生类构造函数的调用次序(1)各基类构造函数的调用(2)内嵌成员对象构造函数的调用(3)派生类构造函数的执行注意事项基类构造的顺序:多个基类构造函数的调用顺序按照基类被继承时声明的顺序,从左向右依次进行。与派生类构造函数初始化列表中基类的出现顺序无关。成员对象的构造函数调用顺序按照它们在类中定义的顺序依次进行。禁止重复继承:C++不允许一个类直接继承自同一个基类两次。多继承析构函数的调用顺序遵循和构造函数完全相反的顺序。class
D:public
A,public
B{public:D():B(1),A(2){}};27【例14-6】在普通的时钟类Clock基础上派生出闹钟类AlarmClock。应用实例class
Clock{private:intH,M,S;public:Clock(int
h=0,int
m=0,int
s=0){H=(h>=0&&h<24)?h:0;M=(m>=0&&m<60)?m:0;S=(s>=0&&s<60)?s:0;
}voidSetTime(int
h,int
m,int
s){H=(h>=0&&h<24)?h:0;M=(m>=0&&m<60)?m:0;S=(s>=0&&s<60)?s:0;
}
voidShowTime(){cout<<H<<
":"
<<M<<
":"
<<S<<endl;}};28应用实例class
AlarmClock:public
Clock{private:
intAH,AM;//响铃的时间
boolOpenAlarm;//是否关闭闹钟public:
voidSetAlarm(int
AH,int
AM)//设置响铃时间{this->AH=AH;this->AM=AM;}
voidSwitchAlarm(bool
Open=true)//打开/关闭闹铃{OpenAlarm=Open;}
void
ShowTime(){//显示当前时间与闹铃时间cout<<
"Clock";
Clock::ShowTime();cout<<
"Alarm"
<<AH<<
":"
<<AM<<endl;
}};覆盖基类中的同名函数调用基类的函数intmain(){
Clockc;c.SetTime(8,30,30);c.ShowTime();
AlarmClockac;ac.SetTime(9,20,0);ac.SetAlarm(10,0);ac.ShowTime();
return0;}运行结果:8:30:30Clock9:20:0Alarm10:0公有继承自Clock类增加属性和方法继承而来派生类扩充派生类改写29【例14-7】多层继承实例应用实例class
Point{private:intX,Y;public:Point(int
X=0,int
Y=0){this->X=X;this->Y=Y;}voidmove(int
OffX,int
OffY){X+=OffX;Y+=OffY;}voidShowXY(){cout<<"("<<X<<","<<Y<<")"<<endl;}};const
doublePI=3.14159;class
Circle:public
Point{protected:doubleradius;public:Circle(double
R,int
X,int
Y):Point(X,Y){radius=R;}doublearea(){returnPI*radius*radius;}voidShowCircle(){cout<<
"Centreofcircle:";ShowXY();cout<<
"radius:"
<<radius<<endl;}};保护成员,可被派生类访问30应用实例class
Cylinder:public
Circle{private:doubleheight;public:Cylinder(int
X,int
Y,double
R,double
H):Circle(R,X,Y){height=H;}doublearea(){return2*Circle::area()+2*PI*radius*height;}doublevolume(){return
Circle::area()*height;}voidShowCylinder(){ShowCircle();cout<<"heightofcylinder:"<<height<<endl;}};覆盖基类Circle中的同名函数调用基类Circle中的area函数Point⬅Circle⬅Cylinderintmain(){CylinderCY(100,200,10,50);CY.ShowCylinder();CY.move(1,2);CY.ShowCircle();cout<<
"totalarea:"
<<CY.area()<<endl;cout<<
"volume:"
<<CY.volume();return0;}运行结果:Centreofcircle:(100,200)radius:10heightofcylinder:50Centreofcircle:(101,202)radius:10totalarea:3769.91volume:15707.9继承自基类的基类继承自基类内容导航继承大模型实践多态大模型探究纯虚函数与抽象类本章小结实践探究多态32类型兼容规则主要涉及基类对象、指针或引用与派生类对象之间的关系。这些规则在运行时确保了对象类型转换和访问的准确性,从而构成了C++中多态性的基础。主要包括以下三种情况:将派生类对象赋给基类对象直接将派生类对象赋给基类对象,只保留其基类的子对象部分,派生类的特有部分被丢弃。一般不建议这样做。基类指针指向派生类对象基类指针指向派生类对象。通过基类指针,可以访问派生类对象中的基类子对象部分。基类的对象引用派生类的对象基类的引用绑定到派生类的对象。通过基类引用,可以访问派生类对象中的基类子对象部分。类型兼容规则33【例14-8】类型兼容演示类型兼容规则classPerson{private:stringname;chargender;intage;public:voidsetName(stringn){name=n;}};classTeacher:publicPerson{private:stringemployeeNo;doublesalary;public:voidsetEmployeeNo(stringn){employeeNo=n;}};classStudent:publicPerson{private:stringstuNo;doublescore;public:voidsetStuNo(stringn){stuNo=n;}};intmain(){Teachert;Personp=t;p.setName("li");Person*ptr=&t;
ptr->setName("li");Students;Person&ref=s;ref.setName("li");
ptr->setEmployeeNo(“123”);//错误:Person中没有该函数ref.setStuNo("123");//错误:Person中没有该函数}基类的对象、指针和引用只能访问基类中定义的成员,不能访问派生类特有的成员。将t的基类子对象赋给p34多态性概念多态性是面向对象编程中的一个关键特性,它允许不同类型的对象收到相同消息(即调用相同成员函数)作出不同的响应,从而展现出不同的行为。有一个动物(Animal)王国,其中包含了多种动物,如狗、猫、鸟(Dog、Cat、Bird)等。当你对这些动物发出“叫”的命令时,每种动物都会以自己独特的方式响应这一命令:狗会汪汪叫,猫会喵喵叫,而鸟则会叽叽喳喳。在C++中,以上动物王国的实现方式为:Animal为基类,Dog、Cat、Bird派生自Animal,它们都有一个成员函数makeSound(),你发出“叫”的命令可以视为对该成员函数的调用,不同的动物类通过各自的makeSound()实现来展现不同的行为,这即为多态性的体现。多态性主要包含两种类型:编译时多态(静态联编)和运行时多态(动态联编)静态联编静态联编,又称为早期绑定,C++中,函数重载和模板实例化是典型的静态联编示例。当调用一个重载函数时,编译器会根据函数参数的类型和数量,在编译时就确定应该调用哪个具体的函数体。同样,模板实例化时,编译器也会根据模板参数的具体类型,生成相应的代码实例。多态性35【例14-9】以下代码实现了动物王国,在main函数中创建了Dog、Cat和Bird对象,并通过Animal对象、Animal*指针和Animal&引用来调用makeSound()函数。多态性classAnimal{public:voidmakeSound(){cout<<"Animalsound"<<endl;}};classDog:publicAnimal{public:voidmakeSound(){cout<<"WoofWoof"<<endl;}};classCat:publicAnimal{public:voidmakeSound(){cout<<"MeowMeow"<<endl;}};classBird:publicAnimal{public:voidmakeSound(){cout<<"ChirpChirp"<<endl;}};intmain(){Dogdog;Catcat;Birdbird;Animalanimal=dog;animal.makeSound();Animal*animalPtr=&cat;animalPtr->makeSound();Animal&animalRef=bird;animalRef.makeSound();}运行结果:AnimalsoundAnimalsoundAnimalsound静态联编的特性:基于变量的声明类型而非运行时实际类型来绑定函数调用。36动态联编与静态联编相对,动态联编(也称为晚期绑定或运行时多态)则允许程序在运行时才决定具体调用哪个函数。这种多态性并非预先在代码编写阶段就固定下来的,而是依赖于程序运行时的上下文环境和对象的实际类型。它允许我们通过统一的接口(即基类指针或引用)来操作不同的对象,而具体执行哪个类的成员函数,则留待运行时决定。在C++中,实现运行时多态性的关键工具是虚函数。多态性37【例14-10】下面修改动物王国的代码,将基类Animal的成员函数makeSound()声明为虚函数多态性classAnimal{public:
virtualvoidmakeSound(){cout<<"Animalsound"<<endl;}};intmain(){Dogdog;Catcat;Birdbird;Animalanimal=dog;animal.makeSound();
Animal*animalPtr=&cat;animalPtr->makeSound();
Animal&animalRef=bird;animalRef.makeSound();return0;}运行结果:AnimalsoundMeowMeowChirpChirp动态联编允许在运行时根据实际的对象类型来决定调用哪个函数。添加virtual声明其余代码均保持不变静态联编动态联编虚函数如同一个承诺,向编译器宣告:在这个类及其任何派生类中,这个函数都可能存在不同的实现。38虚函数定义规则虚函数必须在基类中被声明为virtual,语法格式:classBaseClass{public:
virtual
函数类型函数名(形参表){//函数体;
}};在派生类中重新定义该虚函数时,必须保持函数原型的一致性,包括返回类型、函数名、参数列表(包括参数类型及顺序)均不得更改。虚函数39动态联编的充要条件要实现虚函数的动态联编,即根据对象的实际类型在运行时选择调用哪个函数,必须满足以下四个条件:(1)基类中的函数必须声明为虚函数。(2)派生类必须从基类继承,并重写虚函数。(3)必须通过基类的指针或引用来调用虚函数。(4)基类指针或引用必须指向派生类的对象。虚函数40举例-动态联编四个条件演示虚函数classShape{public: virtualvoidprintShapeName(){cout<<"Shape"<<endl;}};classPoint:publicShape{public: voidprintShapeName(){cout<<"Point"<<endl;}};classCircle:publicPoint{public: voidprintShapeName(){cout<<"Circle"<<endl;}};classCylinder:publicCircle{public: voidprintShapeName(){cout<<"Cylinder"<<endl;}};满足条件1:基类函数被声明为虚函数满足条件2:派生类从基类继承,并重写虚函数说明:基类的虚函数在派生类中重新定义时,自动成为虚函数41举例-动态联编四个条件演示虚函数voiddisplayName(Shape&shape){shape.printShapeName();}intmain(){inti;PointaPoint;CircleaCircle;CylinderaCylinder;Shape*pShape[3]={&aPoint,&aCircle,&aCylinder};cout<<"以下通过指针实现多态:"<<endl;for(i=0;i<3;i++)pShape[i]->printShapeName();cout<<"以下通过引用实现多态:"<<endl;displayName(aPoint);displayName(aCircle);displayName(aCylinder);return0;}运行结果:以下通过指针实现多态:PointCircleCylinder以下通过引用实现多态:PointCircleCylinder满足条件3:通过基类指针和引用调用虚函数4:基类指针和引用实际指向派生类对象42虚函数的特性具有函数体:名“虚”实不虚“虚”的含义指函数调用的具体实现可能依据对象的实际类型而非声明的类型来决定。虚函数的继承当基类中声明了虚函数,这些虚函数可以被其派生类继承。若派生类没有重新定义这些虚函数,则它们在派生类中保持其原有的“虚”特性。在派生类中自动成为虚函数当基类中的虚函数在派生类中被重新定义时,无论派生类中的函数声明是否显式包含virtual关键字,该函数都将自动成为虚函数。虚函数43虚函数的注意事项虚函数不能声明为静态函数静态成员函数属于类本身而非对象。它们不依赖于对象的具体类型,因此不符合虚函数通过对象类型动态选择函数实现的需求。虚函数的声明位置只有类的成员函数才能被声明为虚函数,且这一声明必须出现在类的定义中。普通函数或友元函数不是类的成员函数,不能声明为虚函数。构造函数与析构函数的特殊性构造函数不能是虚函数,因为构造函数在对象完全构造之前就被调用,此时对象的类型还未完全确定,无法确定应调用哪个派生类的构造函数。析构函数通常被声明为虚函数,以确保通过基类指针删除派生类对象时,能够调用到正确的析构函数,从而正确释放资源。虚函数44如果基类指针指向了派生类对象,而析构函数不是虚的,那么在通过基类指针删除对象时,只会调用基类的析构函数,而不会调用派生类的析构函数。C++允许将析构函数声明为虚函数,而且通常声明为虚函数,即虚析构函数class基类名{public:
virtual~基类名(){//清理基类资源
}//其他成员...};class派生类名:public基类名{public:~派生类名(){//先清理派生类资源
//隐式调用基类析构函数
}//其他成员...};虚析构函数当析构函数被声明为虚函数时,通过基类指针删除派生类对象时,将触发动态绑定机制。在派生类的析构过程中,首先调用派生类的析构函数,然后将自动调用基类的析构函数,从而确保从派生类到基类的完整析构链被正确执行运行结果:A::A()iscalled.B::B()iscalled.A::~A()iscalled.45【例14-12】用虚析构函数删除派生类动态对象虚析构函数classA{public:A(){cout<<"A::A()iscalled."<<endl;}~A(){cout<<"A::~A()iscalled."<<endl;}};classB:publicA{private:int*ip;public:B(intsize){ip=newint[size];cout<<"B::B()iscalled."<<endl;}~B(){cout<<"B::~B()iscalled."<<endl;delete[]ip;}};intmain(){A*b=newB(10);deleteb;return0;}运行结果:A::A()iscalled.B::B()iscalled.B::~B()iscalled.A::~A()iscalled.虚析构函数调用派生类的析构函数将virtual删除,会有什么改变?virtual内容导航继承大模型实践多态大模型探究纯虚函数与抽象类本章小结实践探究纯虚函数与抽象类47纯虚函数通过在函数声明的末尾添加=0来标识。在基类中没有具体的函数体,而要求从该基类派生出的子类提供具体的实现。纯虚函数的主要目的是定义一个接口规范,确保所有派生类都遵循这一规范,从而提供一种多态性的机制。语法格式
virtual函数类型函数名(参数表)=0;【例14-13】纯虚函数实例classshape{protected:doublex,y;public:shape(doublexx,doubleyy){x=xx;y=yy;}virtualdoublearea()=0;virtualvoiddisplay(){cout<<"Thisisashape.Thepositionis("<<x<<","<<y<<")\n";}};纯虚函数虚函数,允许派生类重写纯虚函数,派生类必须重写48抽象类的概念如果一个类至少包含一个纯虚函数,则该类被称为抽象类。纯虚函数在基类中没有具体的实现(即没有函数体),使得抽象类成为了一种只能被继承而不能被直接实例化的类。抽象类的特性不能实例化:抽象类不能直接用来创建对象,因为纯虚函数没有实现。不能用作函数参数类型、函数返回类型或显式转换类型:抽象类无法声明具体的对象。作为基类:通过继承抽象类,派生类可以实现基类中所有的纯虚函数,否则派生类本身仍然是抽象类,同样不能被实例化。只有派生类提供了所有纯虚函数的实现,它才可以被实例化或用作函数参数及返回值类型。指针和引用:可以声明指向抽象类的指针或引用,指向抽象类的任何派生类对象,只要这些派生类提供了所有纯虚函数的实现。抽象类49【例14-14】实现一个简单的几何形状管理程序,允许用户输入矩形和圆形的参数,计算并输出所有形状的总面积。具体为:定义一个名为shape的抽象类,随后定义rectangle和circle两个派生类。在main函数中,程序根据用户的输入动态创建派生类的对象。
输入格式为:输入r后跟两个数字(例如510),则创建一个rectangle对象;输入c后跟一个数字(例如10),则创建一个circle对象;输入e时,表示输入结束。这些创建的对象会被添加到一个shape*类型的指针数组中。之后,程序会遍历这个数组,计算并累加所有shape对象的面积,最后输出总面积。抽象类50抽象类classshape{public:virtualdoublearea()=0;};classrectangle:publicshape{doublew,h;public:rectangle(doubleww,doublehh){w=ww;h=hh;}doublearea(){returnw*h;}};classcircle:publicshape{doubler;public:circle(doublerr){r=rr;}doublearea(){return3.14*r*r;}};intmain(){vector<shape*>shapes;chartype;doublew,h,r;while(true){cout<<"请输入形状和参数:";cin>>type;if(type=='e'){break;}elseif(type
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 任务一 驱动电机系统结构原理及认识
- 司马相如的汉赋创作
- 2025-2026月考试卷八年级数学上学期期中模拟卷(沪教版)(原卷版)
- 面向大学生的秦腔文化科普
- DB63∕T 2548-2026 动物疫病防控标准体系
- 2026年消防安全学校活动方案
- 2026年老年人端午节创意活动策划
- 2026年放射科辐射安全应急预案方案
- 2026年班会课日常集体教育活动方案
- 2026年供电公司消除安全隐患
- 精装工程述标演示文稿
- 选矿厂突发环境事件应急预案
- 浅谈新时代少先队红色基因的传承 论文
- 2021-2022学年大连市沙河口区六年级下册小升初考试 数学 试卷(含答案)
- 基于频率法串联超前校正课程设计
- GB/T 3457-1998氧化钨
- GB/T 3405-2011石油苯
- GB/T 2423.2-2008电工电子产品环境试验第2部分:试验方法试验B:高温
- 融合终端MQTT上行app使用说明
- 第三章,海洋生物,的繁殖,与发育
- 2021年安徽省中考地理真题(word解析版)
评论
0/150
提交评论