[音乐]
前面我们学习了这样的一个继承 那么在继承里面了解到了这样的一个继承的
构造函数之间的这样的一个写法 那么我们说了当有一个类通过某个类进行继承的时候
那么创建这样的一个继承的对象,首先要构建一个基类的对象,然后再构建自己
那么在下面的学习当中,我们将着重的介绍一个新的内容:就是多态的问题
基类和派生类之间有相同的
函数到底会发生一些什么样的情况,以及我们有一些什么样的约定 到底会产生一些什么样的调用跟语法。
我们下面将会详细讨论一下这方面的问题 那么在讨论多态之前我们首先说一下函数
或者说方法怎么样算是一样的 如果一个函数,那么它的返回值
以及它的参数个数,函数的名字或者方法的名字
以及这样的一个参数出现的顺序完全一致
也就是说它的函数头或者说它的方法头是一样的话
那么再注意一下,在我们的这个课堂里面,函数跟方法 这两个概念是等同的,都意味着 方法或者函数。
因为在老的 C、 C++,JAVA 这样的系列里面可能 更多的是叫的是函数。
那在我们新的这个.Net 里面更多的是讲的是这个方法
两个在我们的这个课里面指的是同一个概念 那么也就是说如果一个函数的头
包括它的返回值、 方法、 参数、 以及参数序列完全一致的话
我们说这个函数的,这两个函数是一模一样的 也说这两个方法或者函数的签名是一样的
那么如果,如果在基类里面
如果在基类里面有一个这样的一个函数
那么在派生类里面又出现,同样出现了这样
的一个完全相同的,也就是说签名完全一致的函数
会发生什么情况?或者说如果出现了一个仅仅只有函数名相同
但是其返回值或者函数 的参数、
顺序、 个数出现了不一样的情况 又是一种什么样的情况?那么这里面
牵扯到了一些 CSHARP 的基本问题,我们首先来 都来讨论一下。
我们讨论的 第一个问题是抽象类和抽象成员函数
那么,抽象类跟抽象成员函数都要使用这样的一个关键字 abstract 来声明抽象类。
也就是说如果我要声明一个抽象类的话 那么其格式是 public
abstract,然后怎么样呢?再加上这样一个 class,然后才是类名
也就是说在我们原来的这样的一个类名的前面,基础上增加了一个关键字 abstract 抽象类。
那么如果一个类是抽象类的话
那么在此我们规定,那么这个类不能够 被实例化,只允许被继承
也就是说一个抽象类只是用来作为基类
进行继承或者向外派生所用的
不能够把抽象类实例化为一个具体的对象
那么我们知道在很多 现实的编程当中我们有一些比较抽象的基类
把这样的基类初始化成一个实体对象是完全没有必要的,而且也不知道干啥
例如,我们有一个宠物的抽象的基类 从中间可以派生出来猫类啊、 狗类啊
然后的这样一个其它之类的,包括宠物龟啊之类的这样的一些 派生类。
那么我们可以具体化一只猫、 实例化一条狗
或者实例化一个乌龟,但实例化一个宠物就没有什么太大的意义
所以这样的时候,我们可以把这样的一个宠物类声明为抽象的基类
或者我们在一些抽象的类里面含有一些没有实现的 函数,比如说我们的方法,这是我们马上要说的
它只给出了一个函数和方法的签名,但是并没有 给出具体实际的函数或者方法的代码
那么当然了,如果在一个类里面含有某个的函数或者方法没有具体的函数体的话
当然,这样的一个类也是不允许被实例化的。
因为如果这样的类被实例化了的话 那么要调用这个函数的时候,这个函数又没有代码,或者方法又没有代码,那肯定是不行的
所以这样的类也不允许够被实例化 那么同样的 abstract
这样的一个关键字也能够怎么样呢? 也能够修饰一个方法,或者一个函数在前面
那么也就是说在访问修饰符的后面我们可以有 譬如讲
public abstract world 或者 int 什么什么 math
iii 的方法名,然后怎么样呢?后面跟的是这样的一个参数 列表,然后直接就是一个分号
注意看一下幻灯片的这个写的格式 [访问修饰符] abstract
关键字,然后返回值类型,方法名,[参数表] 圆括号之后后面跟的是一个分号
没有这样的一对花括号表示的这样的一个 具体的函数体或者方法体的这样的一个代码
那意味着什么呢? 意味着对于一个抽象方法而言,我们只需要
给出它的这样的一个函数的签名 就可以了,不需要带有任何的方法
呃,具体的这样的一个执行的代码 那么仅仅只要给出它的一个声明就可以了
因此对于一个抽象的方法来说 如果一个类里面含有这样的一个抽象的方法的话
那么这个类就是一个抽象类,这个类怎么样呢?就没有办法被实例化 因为在
这样的一个类里面,这样的一个方法里面,它没有具体的代码
如果允许它实例化的话,这样的一个方法是没有办法被执行的一个方法 那么对于这样的一个
abstract 这样的一个方法又有什么作用呢?
它的作用是:任何从这个类派生出来的类
都必须实现该方法
否则的话就会产生编译的错误而过不去 而没有办法完成编译。
换言之,如果我在 一个抽象的类里面含有一个抽象的这样的一个方法
譬如讲叫 foo 的话。
那么如果某一个类 要从这个抽象类产生出来一个派生类的话
在这个类里面,在它派生的这个类里面 必须含有一个
foo 的这个方法的具体的实现。
也就是 说给出了具体的函数代码的这样的一个方法
在这样的一个派生类里面才能够允许它正确的继承 和通过编译。
那这到底是干了一件什么样的一个事情呢?
那么拿我们刚才的这样的一个举过的一个宠物类的例子再形象的稍微的说明一下
我们刚才说了
我们可以把宠物定义成一个这样的一个基类,对吧?
然后那么从宠物这样的类派生出来一个猫类,派生出来一个狗类
那么假设我希望我所有的这样的一个宠物都是能够发生的 都是能够会叫的。
譬如讲猫可以喵喵的叫 狗可以这样的一个汪汪的叫。
那么也就是说我希望所有的 宠物类里面都有一个可以发声的这样的一个方法
而且我不希望它丢失或者不小心忘记了这个方法
那么这样的一个做法是可以这样来实现:在我的抽象
的宠物的基类里面,就是宠物这样的一个抽象的类里面
我声明一个抽象的发声的这样的一个方法 但是并没有给出具体的函数体。
为什么呢?道理也很简单,因为 不好定义,也没法定义一个很抽象的宠物
该怎么叫,但是一旦在它这个基类的宠物类里面,定义了这样的一个抽象的方法
那么不管是猫从这样的一个宠物类派生,还是狗从这样的一个宠物类派生
它必须要实现这个方法,才能够派生成 功。
也就是说,虽然一个宠物我不知道该怎么叫
但是当派生出来一个具体的猫和狗的时候,他是应该知道该
怎么样发生的,该怎么样叫的,必须还实现这样的一个
函数方法,然后给出什么呢?具体的执行代码 比如讲,狗可以汪汪地叫,猫可以喵喵地叫,对不对?
那么这样子就,就形成了我们这样的一种 强制性的这样的一种机制
[iii],虽然我这样作为一个抽象的,祖宗的类的
鼻祖这样的一个类来讲,宠物来讲,我不知道一个宠物到底该怎么叫
但是我的一个子子孙孙的派生类里面,每 一个具体的宠物都必须会叫才能是
我的派生类,否则就不是宠物 那么就定义了这样的一个事实跟现状
除了这样的一个,abstract 的修饰的这样的一个只有函数签名的
这样的一个 方法之外,它必须在派生类里面被重写之外
那么我们下来还要再看看其他的一些,可以用,用的关键字 那么首先我们在看到这个幻灯片的最底下一行
那么上面有一个 override 和一个 overload 这样的两个这样的一个
关键字,其中第一个关键字,我们中文的意思管它叫做 重写,第二个关键字叫做重载
重载和重写要特别注意一下,区分这两个概念
那么根据我们刚才讲述的这样的一个情况
如果基类里面有这样的一个抽象的函数
在派生类里面,给出了一个它具体的这样的一个实现的代码
这样我们称之为是对这样的一个函数方法的重写 override,并且在写这个
具体的函数的代码的时候,在具体实现这样的一个方法的时候
我们怎么样呢?要添加关键字 override
一定要添加关键字 override
好了 那么什么叫做
overload 呢?就是重载,它的中文,重载
重载是指的这样的一件事情,重载 一般多发生在同一个类中间
注意我们刚才说的这个override,重写,一定发生在
两个类,就是父类跟子类的之间
父类有一个方法,在子类怎么样呢?重写了这个方法
那么这个叫重写。
而重写的一个重要特征是 父类的方法和子类的方法是完全一样的
所谓完全一样,是指的它们方法的签名。
也就是说 返回值,方法名,参数列表,参数
个数,参数的顺序都完全一致的两个方法 称为是签名是一致的。
这样的两个方法之间,能够发生这样的一个 重写,重写。
那什么是重载? 重载一般发生在同一个
类,同一个类里面。
那么当然 它也可以发生在父类跟子类的这样的关系上面,都是可以发生的
那么所谓一个重载的函数仅仅是指
这两个方法具有同样的方法的名称
但是它们的参数列表是不一样的 所谓参数列表不一样,是指的参数的个数不一样
或者参数的个数一样,但是参数的类型不一样
或者参数的个数跟类型都一样,但是参数出现的顺序不一样
总之,只要能够从函数调用的时候 能够看出来到底调用的是哪一个版本的
函数的话,那么就可以说是,这个函数,这些名字一样的这些函数之间怎么样呢? 是重载的关系。
它们虽然它们之间 的名字是一样的,但是它们的签名不一样
也就是说它们具体的这个参数列表是不一样的,我通过这样的分析这个函数调用的时候传- 递过来的
参数个数,参数类型,跟参数出现的顺序,我能够准确 地区分出来到底是哪一个版本的这个函数
要能够被调用,这样的情况称之为函数的重载 重载需要使用关键字
overload 来声明 函数的重载仅仅是方法名或者函数名不一样
而不是整个签名不一样。
那么同时要注意一点的是 如果仅仅只有返回值不同的话
不作为一个区分的依据,所以它总是要使得它的这个参数的列表
有所不同,这就是函数的重写和重载
那么我们再来回忆一下我们到此谈到的这样的一个问题。
我们说了 我们有介绍了一个新的关键字 abstract,抽象。
那么 abstract 可以 修饰一个类叫做一个,形成一个抽象类
如果一个类声明为 abstract 的话,那么
这个类,就不能够产生这个类的实例,这个类只能够被用来被 继承。
同样的,在方法前面,或者函数前面,也能够用 abstract
来怎么样呢?修饰,来修饰 那么 abstract 修饰的这样一个方法和
函数,那么它只有函数的头,或者方法的头 而没有方法体,也就是说不带任何具体的代码
那么到了这个参数列表完了之后,就直接分号结束了
那么它的意思是,在此不知道这个函数该具体的实现,该怎么编写
但是如果想从[iii]派生出一个新类的话,新
类必须提供这样的一个抽象方法的具体实现
才能够正确地派生成功 好了,下来我们来看,先来看一个
例子,我们再往下讲