白马非马
----继承
作者:HolyFire
古人云:“白马非马”,为何如是说呢。
这里有个讲究,白马是指一种白色的马,而非马里的马指的是各种各样的马,有黑马,棕吗,五花马等等,这是在颜色上有所区别,马还有产地,品种等等区别。这里给了我们一个启示,各种各样的马可以找出很多共同点,称之为马,符合这些特点的动物就是马了,所以这些共同点不能多也不能少,多了不能同用于所有的马,少了表达不出马来。要区别一只马需要很多信息,比如一只黑色的蒙古马,在这里,我除了描述一只马外,还要指出它是黑色的,产地是蒙古,这里还没有加上马的品种。
我在《芥子须弥-----封装》里曾经提及,事物可以划分成属性和方法的集合。那么我们是不是可以对这个集合进行再加工呢,我们只要在里面加上一点别的什么,那他就可以变成别的事物了,想象一下在马的身上加上鹿的角,呵呵呵呵,可笑吗。那么我们说一些严肃的,知道我们中华民族的图腾吗,龙!龙是在蛇的头上加上鹿的角,身上加上虎的腿脚,尾巴是鱼的尾巴,等等。但是不是随随便便就能造就龙这样伟大的象征的,不好好设计,就会成为画蛇添足。
虽然是简单和相加,但也是有不同的方式的。一般来说有两种方式:关联和衍生。
关联:假设原来的类是用一个袋子将属性和方法包起来,那是用关联描述了多个类之间的关系,最简单的情况就是用一个更大的袋子将两个小袋子包起来,成为一个新的类。这种关联叫做组合。
衍生:衍生呢就是用一个大的袋子将一个小的袋子与一些属性和方法包起来,成为一个新的类
A
C
B
a:关联
A
D
b:衍生
我还是用C++来表示一下
class A{
//...属性+方法
};
class B{
//...属性+方法
};
//关联后的新类C
class C{
A a;
B b;
};
//衍生后的新类D
class D : public A{
//...属性+方法
};
看起来关联(组合)比较容易理解,即将类作为一个属性,然后形成一个新的类,这和原来类的使用没什么区别,自己定义的类就是一个类型,这正是封装要做的。
衍生—在原来的类的基础上加上一些属性和方法产生一个新类,看起来新颖诱人,在面包上洒一些芝麻,吃起来更有香味,不是吗。
如何好好利用这一方法,就是我要说的----继承,只要用心去做,面包也会非常美味。(这里提及的都是共有继承也就是class Y: public X;的形式)
上面的方法说起来简单,要是用好它,不是件容易的事。
首先D和A有相同的部分就是A
第二,D有A没有的部分
光光这两点还说明不了继承的用途
我们加上一个新类E,他也是与类A衍生得来的
class E : public A{
//...属性+方法
};
这样我们就清楚一点了,A是D和E的共同点
耶~~~~!我们终于发现这样做的好处了。
类D和类E有相同的部分A,而这里A只被处理了一次,也就是说我们可以少处理一个和A一模一样的工作,如果有许多个类都是从A衍生而来的,那么我们就发达了,问题是,如何让更多的类可以与类A衍生得到有用并好用的类。
这里老祖宗又显灵了,白马非马说的就是这个道理,马正是这个类A,加上颜色,产地,品种,就可以描述各种各样的马了,加上健康等信息还能说明马的状态,如一只活蹦乱跳的黑色蒙古马。
在我们的知识里有很多已知的共同点,碗,瓷器,风,人,花草,我们将他们成为统称。
我们可以下一个定义了:继承--就是将事物之间相同和相似的部分归纳出来。
我们来看一下基类A和子类D的关系,可以发现D中含有A,也就是说可以从一个D类的对象中提取出一个A类的对象来,这种现象就是继承的向下性,马可以是白马,而白马就不能说成是马这个种类的代表。表现在C++中如何处理呢。
D d;
A a = d;
这里将会产成一个A类型的临时变量,它是由d中类A那部分组成的
[
d的组成
[类A的部分] --à 临时变量da;
…
]
A a = da;
这种现象叫做切片
再看另一种情况
D * d = new D;
A * a = d;
这时候A * a = d;只涉及到指针的赋值,没有对象的创建,所以不会引起切片,这里是将d在内存中的地址保存在a这个指针里,而描述指针指向的对象是一个类A的对象。由于,D中关联A的信息,C++编译器使得这样的操作是合法的,也是可以理解的。
我们可以把继承A的D称作为A的某个类型,是类A的一种(或者说,D是一个A),就象白马是一种马一样(白马是一匹马)。
继承非常强大,使用它能得到很多好处,但是世界上没有包治百病的,过分的运用继承也会带来麻烦。可以看出继承是一种静态的关系,在编译期间很多东西都定下来了,这也提高了效率,但是要改变很困难,所以灵活性还不够,要想清楚,你的目的是什么,再决定使用关联还是继承。
2001/8/15
丁宁