荔园在线

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

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


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

================================
● 12D:继承--建构子与解构子
================================

Q66:若基底类别的建构子呼叫一个虚拟函数,为什麽衍生类别覆盖掉的那个虚拟函
     数却不会被呼叫到?

在基底类别 Base 的建构子执行过程中,该物件还不是属於衍生 Derived 的,所以

如果 "Base::Base()" 呼叫了虚拟函数 "virt()",则 "Base::virt()" 会被呼叫

即使真的有 "Derived::virt()"。

类似的道理,当 Base 的解构子执行时,该物件不再是个 Derived 了,所以当
Base::~Base() 呼叫 "virt()",则 "Base::virt()" 会被执行,而非覆盖後的版本

"Derived::virt()"。

当你想像到:如果 "Derived::virt()" 碰得到 Derived 类别的物件成员,会造成

麽样的灾难,你很快就会看出这规则的明智之处。

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

Q67:衍生类别的解构子应该外显地呼叫基底的解构子吗?

不要,绝对不要外显地呼叫解构子(「绝对不要」指的是「几乎完全不要」)。

衍生类别的解构子(不管你是否明显定义过)会“自动”去呼叫成员物件的、以及基

底类别之子物件的解构子。成员物件会以它们在类别中出现的相反顺序解构,接下来

是基底类别的子物件,以它们出现在类别基底列表的相反顺序解构之。

只有在极为特殊的情况下,你才应外显地呼叫解构子,像是:解构一个由「新放入的

new 运算子」配置的物件。

===========================================
● 12E:继承--Private 与 protected 继承
===========================================

Q68:该怎麽表达出「私有继承」(private inheritance)?

用 ": private" 来代替 ": public."  譬如:

        class Foo : private Bar {
          //...
        };

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

Q69:「私有继承」和「成份」(composition) 有多类似?

私有继承是「成份」(has-a) 的一种语法变形。

譬如:「汽车有引擎」("car has-a engine") 关系可用成份来表达:

        class Engine {
        public:
          Engine(int numCylinders);
          void start();                 //starts
 this Engine
        };

        class Car {
        public:
          Car() : e_(8) { }             //initial
izes this Car with 8 cylinders
          void start() { e_.start(); }  //start this Car
by starting its engine
        private:
          Engine e_;
        };

同样的 "has-a" 关系也可用私有继承来表达:

        class Car : private Engine {
        public:
          Car() : Engine(8) { }         //initializes
 this Car with 8 cylinders
          Engine::start;                //start t
his Car by starting its engine
        };

这两种型式的成份有几分相似性:
 * 这两种情况之下,Car 只含有一个 Engine 成员物件。
 * 两种情况都不能让(外界)使用者由 Car* 转换成 Engine* 。

也有几个不同点:
 * 如果你想要让每个 Car 都含有数个 Engine 的话,就得用第一个型式。
 * 第二个型式可能会导致不必要的多重继承(multiple inheritance)。
 * 第二个型式允许 Car 的成员从 Car* 转换成 Engine* 。
 * 第二个型式可存取到基底类别的 "protected" 成员。
 * 第二个型式允许 Car 覆盖掉 Engine 的虚拟函数。

注意:私有继承通常是用来获得基底类别 "protected:" 成员的存取权力,但这通常

只是个短程的解决方案。

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

Q70:我比较该用哪一种:成份还是私有继承?

成份。

正常情形下,你不希望存取到太多其他类别的内部,但私有继承会给你这些额外的权

力(与责任)。不过私有继承不是洪水猛兽;它只是得多花心力去维护罢了,因为它

增加了别人动到你的东西、让你的程式出差错的机会。

合法而长程地使用私有继承的时机是:当你想新建一个 Fred 类别,它会用到 Wilm
a
类别的程式码,而且 Wilma 的程式码也会呼叫到你这个 Fred 类别里的运作行为时

。这种情形之下,Fred 呼叫了 Wilma 的非虚拟函数,Wilma 也呼叫了它自己的、

被 Fred 所覆盖的虚拟函数(通常是纯虚拟函数)。要用成份来做的话,太难了。


        class Wilma {
        protected:
          void fredCallsWilma()
            { cout << "Wilma::fredCallsWilma()\n"; wilmaCall
sFred(); }
          virtual void wilmaCallsFred() = 0;
        };

        class Fred : private Wilma {
        public:
          void barney()
            { cout << "Fred::barney()\n"; Wilma::fredCallsWi
lma(); }
        protected:
          virtual void wilmaCallsFred()
            { cout << "Fred::wilmaCallsFred()\n"; }
        };

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

Q71:我应该用指标转型方法,把「私有」衍生类别转成它的基底吗?

当然不该。

以私有衍生类别的运作行为、夥伴来看,从它上溯到基底类别的关系为已知的,所以

从 PrivatelyDer* 往上转换成 Base*(或是从 PrivatelyDer& 到 Base&)是安
全的
;强制转型是不需要也不鼓励的。

然而用 PrivateDer 的人应该避免这种不安全的转换,因为此乃立足於 PrivateDer

的 "private" 决定,这个决定很容易在日後不经察觉就改变了。

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

Q72:保护继承 (protected inheritance) 和私有继承有何关连?

相似处:两者都能覆盖掉私有/保护基底类别的虚拟函数,两者都不把衍生的类别视

为“一种”基底类别。

不相似处:保护继承可让衍生类别的衍生类别知道它的继承关系(把实行细节显现出

来)。它有好处(允许保护继承类别的子类别,藉这项关系来使用保护基底类别),

也有代价(保护继承的类别,无法既想改变这种关系,而又不破坏到进一步的衍生类

别)。

保护继承使用 ": protected" 这种语法:

        class Car : protected Engine {
          //...
        };

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

Q73:"private" 和 "protected" 的存取规则是什麽?

拿底下这些类别当例子:

        class B                    { /*...*/
};
        class D_priv : private   B { /*...*/ };
        class D_prot : protected B { /*...*/ };
        class D_publ : public    B { /*...*/ };
        class UserClass            { B b; /*...*/ }
;

没有一个子类别能存取到 B 的 private 部份。
在 D_priv 内,B 的 public 和 protected 部份都变成 "private"。
在 D_prot 内,B 的 public 和 protected 部份都变成 "protected"。
在 D_publ 内,B 的 public 部份还是 public,protected 还是 protected

 (D_publ is-a-kind-of-a B) 。
Class "UserClass" 只能存取 B 的 public 部份,也就是:把 UserClass 从
 B 那
儿封起来了。

欲把 B 的 public 成员在 D_priv 或 D_prot 内也变成 public,只要在该成
员的名
字前面加上 "B::"。譬如:想让 "B::f(int,float)" 成员在 D_prot 内也是 pu
blic
的话,照这样写:

        class D_prot : protected B {
        public:
          B::f;    //注意:不是写成 "B::f(int,float)"
        };

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

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


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

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