荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Second (石开), 信区: Program
标  题: C++语言常见问题解答(2-12)继承
发信站: 荔园晨风BBS站 (Sun Sep 23 10:50:03 2001), 转信

=====================
■□ 第12节:继承
=====================

Q50:「继承」对 C++ 来说很重要吗?

是的。

「继承」是抽象化资料型态(abstract data type, ADT)与 OOP 的一大分野。


========================================

Q51:何时该用继承?

做为一个「特异化」(specialization) 的机制。

人类以两种角度来抽象化事物:「部份」(part-of) 和「种类」(kind-of)。福特汽

车“是一种”(is-a-kind-of-a) 车子,福特汽车“有”(has-a) 引擎、轮胎……等

等零件。「部份」的层次随著 ADT 的流行,已成为软体系统的一份子了;而「继承

」则添入了“另一个”重要的软体分解角度。

========================================

Q52:怎样在 C++ 中表现出继承?

用 ": public" 语法:

        class Car : public Vehicle {
                //^^^^^^^^---- ": public" 读作「是一种」(
"is-a-kind-of-a")
          //...
        };

我们以几种方式来描述上面的关系:

 * Car 是「一种」("a kind of a") Vehicle
 * Car 乃「衍生自」("derived from") Vehicle
 * Car 是个「特异化的」("a specialized") Vehicle
 * Car 是 Vehicle 的「子类别」("subclass")
 * Vehicle 是 Car 的「基底类别」("base class")
 * Vehicle 是 Car 的「父类别」("superclass") (这不是 C++ 界常用的说
法)
   【译注】"superclass" 是 Smalltalk 语言的关键字。

========================================

Q53:把衍生类别的指标转型成指向它的基底,可以吗?

可以。

衍生类别是该基底类别的特异化版本(衍生者「是一种」("a-kind-of") 基底)。这

种向上的转换是绝对安全的,而且常常会发生(如果我指向一个汽车 Car,实际上我

是指向一个车子 Vehicle):

        void f(Vehicle* v);
        void g(Car* c) { f(c); }        //绝对很安全;不
需要转型

注意:在这里我们假设的是 "public" 的继承;後面会再提到「另一种」"private/

protected" 的继承。

========================================

Q54:Derived* --> Base* 是正常的;那为什麽 Derived** --> Base** 则否?


C++ 让 Derived* 能转型到 Base*,是因为衍生的物件「是一种」基底的物件。然

想由 Derived** 转型到 Base** 则是错误的!要是能够的话,Base** 就可能会被

参用(产生一个 Base*),该 Base* 就可能指向另一个“不一样的”衍生类别,这

是不对的。

照此看来,衍生类别的阵列就「不是一种」基底类别的阵列。在 Paradigm Shift 公

司的 C++ 训练课程里,我们用底下的例子来比喻:

               "一袋苹果「不是」一袋水果".
               "A bag of apples is NOT a bag of frui
t".

如果一袋苹果可以当成一袋水果来传递,别人就可能把香蕉放到苹果袋里头去!

========================================

Q55:衍生类别的阵列「不是」基底的阵列,是否表示阵列不好?

没错,「阵列很烂」(开玩笑的 :-) 。

C++ 内建的阵列有一个不易察觉的问题。想一想:

        void f(Base* arrayOfBase)
        {
          arrayOfBase[3].memberfn();
        }

        main()
        {
          Derived arrayOfDerived[10];
          f(arrayOfDerived);
        }

编译器认为这完全是型别安全的,因为由 Derived* 转换到 Base* 是正常的。但事

实上这很差劲:因为 Derived 可能会比 Base 还要大,f() 里头的阵列索引不光

没有型别安全,甚至还可能没指到真正的物件呢!通常它会指到某个倒楣的
Derived 物件的中间去。

根本的问题在於:C++ 不能分辨出「指向一个东西」和「指向一个阵列」。很自然的

,这是 C++“继承”自 C 语言的特徵。

注意:如果我们用的是一个像阵列的「类别」而非最原始的阵列(譬如:"Array"
而非 "T[]"),这问题就可以在编译期被挑出来,而非在执行的时候。

--
                            既然热爱生命
                            那么,
                            一切都在意料之中。

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.52.239]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店