版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第8章继承与多态
本章将继续深入介绍继承和多态两个重要思想以及在C#中的实现,并详细介绍由继承和封装而衍生出的抽象类、抽象函数、密封类、密封函数、运算符重载、虚函数等重要的知识点。章节内容8.1继承的基础知识8.2派生类的构造函数和析构函数8.3抽象类和抽象函数8.4密封类和密封方法8.5多态的基础知识8.6虚方法8.7运算符重载8.8接口8.9小结8.1.1简单继承C++中的多重继承8.1.1简单继承C#为什么摒弃了多继承?C#允许通过多个接口来变相地实现有控制的多继承8.1.2使用继承publicclassMobile //定义基类Mobile{publicMobile() //基类的构造函数
{Console.WriteLine("Iamthebaseclass,Mobile.");}publicMobile(stringname) //基类的构造函数
{Console.WriteLine("Hello,Mynameis"+name+"!");}publicvoidMemorySize(intsize){Console.WriteLine("MemorySizeis"+size+"Icanstorealot.");}}8.1.2使用继承publicclassMoto:Mobile//定义派生类Moto{publicMoto() //派生类的构造函数
{Console.WriteLine("Iamthederivedclass,Moto.");}}classProgram{staticvoidMain(string[]args){Motom=newMoto();m.MemorySize(100);Console.ReadLine();}8.1.3在派生类中使用基类的方法classCustomerAccount{publicdecimalCalculatePrice(){//实现代码
return0.0M;}}classGoldAccount:CustomerAccount{publicdecimalCalculatePrice(){returnbase.CalculatePrice()*0.9M;}}8.2派生类的构造函数和析构函数构造函数的调用顺序为,先调用基类的构造函数,再调用派生类中的构造函数体。因为基类没有不带参数的构造函数,所以即使是派生类的不带参数的构造函数,也必须显式调用基类的带参数的构造函数,尽管该参数本身毫无意义。对基类构造函数的调用必须使用base关键字,而不能像C++一样用具体的类名代替。析构函数的调用顺序同构造函数的调用顺序完全相反。8.2派生类的构造函数和析构函数usingSystem;publicclassMobile{stringparentString;publicMobile(){Console.WriteLine("MobileConstructor.");}publicMobile(stringmyString){parentString=myString;Console.WriteLine(parentString);}publicvoidPrint(){Console.WriteLine("I'maMobileClass.");}}8.2派生类的构造函数和析构函数publicclassMoto:Mobile{
publicMoto():base("FromDerived"){Console.WriteLine("MotoConstructor.");}publicvoidPrint(){base.Print();Console.WriteLine("I'maMotoClass.");}publicstaticvoidMain(){Motomoto=newMoto();moto.Print();((Mobile)moto).Print();Console.ReadLine();}}程序运行结果:FromDerivedMotoConstructor.I'maMobileClass.I'maMotoClass.I'maMobileClass.8.3抽象类和抽象函数抽象类表示一种抽象的概念,用于为其派生类提供一个公共接口。抽象类由abstract修饰符声明。抽象类只能作为其他类的基类,而不能直接实例化,对抽象类使用new关键字会发生错误。抽象函数没有实现代码,而是由abstract修饰符声明。抽象函数只提供函数名称,具体实现交由继承的派生类。可见,抽象函数同抽象类类似,也是虚拟的,但是这里的虚拟不需要而且绝对不能使用virtual关键字(即abstract和virtual修饰符不能一起使用),否则会产生语法错误。8.3抽象类和抽象函数抽象类可以包含抽象函数,也可包含非抽象函数,而非抽象类不能包含抽象函数。也就是说,包含了抽象函数的类一定是抽象类(假如抽象函数可以包含在非抽象类中,那么非抽象类实例调用没有实现代码的抽象函数时,显然不合理)。当从抽象类派生非抽象类时,非抽象类必须具体实现所继承的所有抽象函数,且必须使用override关键字重写抽象类所定义的函数。抽象类可以被抽象类所继承,结果可以仍是抽象类。抽象方法被实现后,不能更改原方法的修饰符。在C++中,对抽象函数有纯虚函数一说;在C#中,仅使用抽象这个术语。8.3抽象类和抽象函数usingSystem;publicabstractclassPerson{publicabstractvoidSayHello();publicvoidAbout(){Console.WriteLine("AbstractDemo");}}publicclassStudent:Person{publicoverridevoidSayHello(){Console.WriteLine("SayHello");}}8.4密封类和密封方法当类和方法被sealed修饰时,就成为了密封类和密封方法。对类来说,这表示不能继承该类;对于方法来说,这表示不能重写该方法。实际上,Java中也有类似的定义,但使用的关键字为final。对于密封类和密封方法的应用场合通常是商业上保护版权,防止被他人扩展。8.4密封类和密封方法classBaseClass{publicvirtualvoidFinalMethod(){ //代码}
}classMyClass:BaseClass{publicsealedoverridevoidFinalMethod(){//代码} }classDerivedClass:MyClass{publicoverridevoidFinalMethod() //错误!
{//代码}}8.5多态的基础知识多态性(Polymorphism)是一个希腊单词,指“同一个名称,多种形态”。或者可以理解为,对同一个名称,可以有不同的理解,有不同的反应。例如,当理发师听到“cut”的时候会开始剪头发,演员听到“cut”的时候会停止表演,医生听到“cut”的时候会在病人身上进行手术。在面向对象编程中,多态是指调用同一个方法名,却执行不同的方法体。多态性是类为方法(这些方法以相同的名称调用)提供不同实现方式的能力。多态性允许对类的某个方法进行调用而无需考虑该方法所提供的特定实现。8.5多态的基础知识在C#中,多态可以分为静态多态和动态多态。静态多态就是方法的重载(overload),也称为静态联编,由编译器在编译期间确定真正执行方法的地址。方法重载和运算符重载(关于运算符重载的介绍可参见本章8.7节)都实现了编译时的多态性。对于方法重载,需要:方法名必须相同。参数列表必须不相同(参数个数不同、参数类型不同或参数个数与类型都不相同)。和返回值类型无关。8.5多态的基础知识动态多态,也称为动态联编,指的是系统在编译期间无法确定真正执行方法的地址,在运行期间由运行时(CommonLanguageRuntime,CLR)根据虚方法表可动态计算出来。动态多态通过虚方法的重写(override)来实现(关于虚方法的介绍可详见本章8.6节)。编译时的多态性能够提供运行速度快的特点,而运行时的多态性能够提供高度灵活和抽象的特点。8.6虚方法对于类中的方法,若声明时加上virtual修饰符,则称该方法为虚方法,否则为非虚方法。和Java不同,Java中的方法默认情况下都是虚方法;C#中类的方法默认情况下都是非虚方法。在基类中把某个方法声明为虚方法,在派生类中就可以通过在该方法名前加override关键字实现对基类中该方法的重写虚方法和其他一般方法有较大的区别。一般方法在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间不发生变化;而虚方法在编译期间不被静态编译,它的相对地址是不确定的,会根据运行时期对象实例来动态判断要调用的方法。8.6虚方法关于虚方法,需要注意:不仅仅只有方法可以虚拟,属性、事件和索引器成员都可以虚拟,但字段不能是虚拟的。虚成员的访问修饰符不能定义为私有。使用了virtual修饰符后,不允许再有static、abstract或override修饰符。使用了override修饰符后,不允许再有new、static或virtual修饰符。在派生类中重写虚方法时,要求方法名称、返回值类型、参数表中的参数个数、类型、顺序、访问修饰符都必须与基类中的虚方法完全一致。只有虚方法和抽象方法才能被重写。8.6虚方法usingSystem;classA{publicvirtualvoidMethod(){Console.WriteLine("A.method");}}classB:A{publicnewvirtualvoidMethod(){Console.WriteLine("B.method");}}classC:B{publicoverridevoidMethod(){Console.WriteLine("C.method");}publicstaticvoidMain(){Aa=newA();Bb=newC();Ac=b;a.Method();b.Method();c.Method();Console.ReadLine();}}执行结果:A.methodC.methodA.method8.6虚方法classC:B{publicoverridevoidMethod(){Console.WriteLine("C.method");}publicstaticvoidMain(){Aa=newA();Bb=newC();Ac=b;a.Method();b.Method();c.Method();Console.ReadLine();}}8.6虚方法关于虚方法,需要注意:不仅仅只有方法可以虚拟,属性、事件和索引器成员都可以虚拟,但字段不能是虚拟的。虚成员的访问修饰符不能定义为私有。使用了virtual修饰符后,不允许再有static、abstract或override修饰符。使用了override修饰符后,不允许再有new、static或virtual修饰符。在派生类中重写虚方法时,要求方法名称、返回值类型、参数表中的参数个数、类型、顺序、访问修饰符都必须与基类中的虚方法完全一致。只有虚方法和抽象方法才能被重写。8.7运算符重载运算符重载是指允许用户使用自定义的类型编写表达式,允许用户定义的类型与预定义的类型具有相同的功能,是对重载概念的一个重要补充和发展,它针对对象关系中的多元关系、四则运算和关系运算等常规运算提供了重载支持。C#中定义运算符重载的语法形式如下:
publicstatic返回值类型operator<operatorName>(<paramlist>)注意,运算符重载的方法必须是public和static,operator是关键字,opertorName是要重载的运算符。8.7运算符重载classcomplex{privatedoublereal,imag;//复数实部和虚部
publiccomplex(doublea,intb){real=a;imag=b;}
publicstaticcomplexoperator++(complexc){c.real++;c.imag++;returnc;}publicvoidDisplay(){Console.WriteLine("complex.real={0},complex.imag={1}",real,imag);}
8.7运算符重载
publicstaticcomplexoperator+(complexc1,complexc2){complexc=newcomplex(0,0);c.real=c1.real+c2.real;c.imag=c1.imag+c2.imag;returnc;}staticvoidMain(string[]args){complexc1=newcomplex(10,20);complexc2=newcomplex(30,40);c1=c1+c2;c1.Display();c1++;c1.Display();Console.ReadLine();}}代码执行结果:complex.real=40,complex.real=60complex.real=41,complex.real=618.7运算符重载并非所有的运算符都可以重载,运算符重载规则如下:+、-、!、~、++、--、true、false,这些一元运算符可被重载。+、-、*、/、%、&、|、^、<<、>>,这些二元运算符可被重载。==、!=、<、>、<=、>=,这些关系运算符可被重载。&&、||,这两个条件运算符不能被重载,但它们的值能够被“&”和“|”评估,而这两个运算符可以被重载。数组运算符([])不能被重载,可以定义索引器。转换运算符(())不能被重载,可以定义隐式类型转换和显式类型转换运算符。+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=,这些赋值运算符不能被重载,但它们的值(如+=)会被“+”评估,而“+”可以被重载。=、.、?:、->、new、is、sizeof、typeof,这些运算符不能被重载。8.8接口与类相同,接口也定义了一些方法、属性、索引和事件。但与类不同的是,接口并不提供实现,它只是一种约定,实现接口的类或结构必须遵守该接口定义的约定。一个接口可以从多个基接口继承,而一个类或结构可以实现多个接口。当类或结构继承接口时,意味着该类或结构为该接口定义的所有成员提供实现。接口本身不提供类或结构能够以继承基类功能的方式继承的任何功能。但是,如果基类实现接口,派生类将继承该实现。接口是体现面向对象编程思想优越性的一件利器。接口是为继承而存在的,如果没有继承,那就自然不需要接口了。既然有继承,就需要把可能被多个类所继承的一些公共部分抽象出来,接口封装的就是这些公共的行为规范(方法定义),类可以通过继承多个接口来丰富自己的行为机制。但是,在C#中,类是不可以继承多个类的,所以接口的变相多继承作用就显得灵活而至关重要。接口可以包含一个或多个成员,这些成员可以是方法、属性、索引指示器或事件,但不能是常量、域、操作符、构造函数或析构函数。接口的成员是从基接口继承的成员和由接口本身定义的成员。接口成员默认访问方式是public。接口成员定义不能包含任何修饰符,比如成员定义前不能加abstract、public、protected、internal、private、virtual、override或static修饰符。接口中不包含任何程序代码,也不能有“{}”。接口不能包含构造函数、析构函数和静态成员。接口不能直接实例化。8.8接口usingSystem;interfaceIProduct{intGetPrice(intid);voidShowName(stringname);}interfaceISize{intGetSize();}8.8接口classShoe:IProduct,ISize{publicintGetPrice(intid){if(id==1)return50;elsereturn100;}publicvoidShowName(stringname){Console.WriteLine(name);}publicintGetSize(){return35;}}8.8接口接口和第7章介绍的抽象类既有相似之处,也有不同之处。其相同之处如下。都不能被直接实例化,都可以通过继承实现其抽象方法
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 林地修路合同范例
- 专项委托设计合同范例
- 单位物业托管合同范例
- 培训包过合同范例
- 安全专篇合同范例
- 铜仁幼儿师范高等专科学校《数据分析与挖掘》2023-2024学年第一学期期末试卷
- 阳江2024年广东阳江市中医医院招聘核电项目组工作人员历年参考题库(频考版)含答案解析
- 通化师范学院《工程经济学B》2023-2024学年第一学期期末试卷
- 铁门关职业技术学院《化工设计》2023-2024学年第一学期期末试卷
- 小学数学二年级第二学期口算计算共5055道题
- 通信线路高风险作业施工安全操作须知样本
- 2024年考研英语真题及答案(完整版)
- 注塑产品成本计算
- 苏教版五年级数学上册第三单元达标测试卷含答案
- 积分上链方案
- JC-T 753-2001 硅质玻璃原料化学分析方法
- 沈阳职业技术学院单招《职业技能测试》参考试题库(含答案)
- 高等数学课件第一章函数与极限
- 黄石市黄石港区2023-2024学年八年级上学期期末数学测评卷(含答案)
- 孤独症abc量表孤独症儿童行为量表ABC量表
- 英语主格宾格及其练习题
评论
0/150
提交评论