上一篇我们涉及了面向对象的一个基本概念--封装,封装是一个相对比较简单的概念,也很容易接受,但是很多的场合下面,仅仅是封装并不能很好的解决很多问题,考虑下面的例子: 假设我们需要设计一个对战游戏的战斗细节,在最初的版本中我们将支持一种动作--fight。假设我们有三种角色:fighter、knight和warrior,每种角色的health、hit point不同,基于封装的基本想法,我们很自然的想到对每个对象使用类的封装,首先明显的元素有2个:health、hit point,另外还有name(我们不能只有三个角色)和战斗的速度speed,方法有:hit、isalive。基于这样的想法,我们给出下面的类: |
|
上面的类可以清楚的抽象出我们所需要表达的数据类型之一,这里也许你已经发现了一些问题: 成员函数hit用来处理战斗事件,但是我们有不同的角色,fighter不可能只跟自己同样的对手作战,我们希望它能和任何角色战斗,当然,使用模板函数可以简单的解决这个问题。另外,我们必须去实现三个不同的类,并且这些类必须都实现这些属性和方法。即使这些问题我们都解决了,现在我们要组织两个队伍作战,我们希望使用一种群体类型来描述它们,问题是我们必须针对每一种类建立相应的群体结构,当然你可以认为3个不同的类型不是很多,完全可以应付,那么如果有一天系统升级,你需要管理上百种类型的时候,会不会头大呢? 在C++中,继承就可以很好的解决这个问题,在C++中,继承表示的是一种IS-A关系,即派生类IS-A基类,很多现实世界中的关系可以这样来描述,如:dog is-a animal,dog是animal的派生(继承),继承产生的对象拥有父(基)对象的所有属性和行为,如animal的所有属性和行为在dog身上都会有相应的表现。在UML的描述中,这种关系被称为泛化(generalization)。一般情况下,当我们需要实现一系列相似(具有一定的共性)然而有彼此不同的类别的时候,使用继承都是很好的解决办法,例如前面的代码虽然也能够实现我们的目标,但是显然很难管理,下面给出使用继承后的实现: |
|
C++为我们提供了非常优秀的继承体系(其实这是面向对象的一个非常重要的特征),上面的类图中我们可以很清楚的看到他们的关系,在继承中,基类(有些地方也称为超类)其实是所有派生类的共性提炼的结果,有时候在开发过程中,是先有派生类,然后再提出共性,产生基类的。 就象遗传一样,派生类拥有基类的所有属性和方法,如基类actor的成员函数和成员变量在每一个派生类中都存在,即fighter、knight和warrior中都存在,在继承关系中还存在一个非常重要的关键字protected,它提供一个界于public和private 之间的一种可见性,与private相同的地方是对于外部来说,它不可见,与public相同的地方是对与继承体系来说,是向下可见的(其派生出来的类可以直接使用),这里有一点是很容易让人迷惑的,就是派生类中基类的成员可见性。对派生类来说,如果使用public继承,那么从基类中继承的所有成员的可见性不变(如果是protected继承,降一级,public变成protected,private继承再降),那么对于一个从基类继承来的private成员来说,派生类是无法访问的(很迷惑,是么?),虽然派生类含有这个变量,但是这是一种间接的拥有关系,在派生类中,含有一个基类的子对象,派生类对基类成员的访问正是通过这个子对象进行的。在思想中,永远不要把继承来的成员看做是自己真正拥有的,虽然使用this可以直接"看到"它们,在派生体系中private和public与其它情况没有任何区别,而protected在体系的内部就和public完全等同。 由于派生类拥有基类的成员,所以我们可以通过派生而简单的"重用"我们已有的代码,从而大大减少重复劳动。 关于继承的另外一个重要的特征就是虚函数,虚函数是形成类的多态的基础,在上面的类中: void knight::hit(actor *Actor) 比如下面的伪码: |
|
这里pA是一个基类的指针,而它指向的是一个派生类knight的指针,调用它应该是怎么样的情况呢?答案是hit会调用knight的方法,而不是基类的,因为它是一个虚函数,虚函数的特点是它永远忠实与实际的对象(前提是正确的使用),通过这种多态的特性,我们可以使用基类来对派生类进行正确的操作,这就解决了一个上面的问题:使用一种群体类型来描述它们,所以我们可以这样来遍历数据,而不需要关心里面究竟是一些什么样的数据: |
|
面向对象思想中的继承是非常重要的概念,正确的运用它,会为软件的开发过程带来很多便利,同时,COM的核心技术是使用了多重继承来实现的。同时,继承也是面向对象的思想中较难理解的一个概念,这片文章我只能大致的讲述其运用,而真正的融会贯通还需要长时间的学习和实践。 下面我们给出上面的例子的完整代码,这段代码完成了随机建立两个队伍,并进行战斗,直到有一个队伍全军覆没,最后输出结果。 |
C++编程杂谈之三:面向对象(续)
最新推荐文章于 2024-08-28 14:35:43 发布