⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 00000001.htm

📁 一份很好的linux入门资料
💻 HTM
📖 第 1 页 / 共 5 页
字号:
车“是一种”(is-a-kind-of-a)&nbsp;车子,福特汽车“有”(has-a)&nbsp;引擎、轮胎……等&nbsp;<BR>等零件。「部份」的层次随著&nbsp;ADT&nbsp;的流行,已成为软体系统的一份子了;而「继承&nbsp;<BR>」则添入了“另一个”重要的软体分解角度。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q52:怎样在&nbsp;C++&nbsp;中表现出继承?&nbsp;<BR>&nbsp;<BR>用&nbsp;&quot;:&nbsp;public&quot;&nbsp;语法:&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;Car&nbsp;:&nbsp;public&nbsp;Vehicle&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//^^^^^^^^----&nbsp;&quot;:&nbsp;public&quot;&nbsp;读作「是一种」(&quot;is-a-kind-of-a&quot;)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};&nbsp;<BR>&nbsp;<BR>我们以几种方式来描述上面的关系:&nbsp;<BR>&nbsp;<BR>&nbsp;*&nbsp;Car&nbsp;是「一种」(&quot;a&nbsp;kind&nbsp;of&nbsp;a&quot;)&nbsp;Vehicle&nbsp;<BR>&nbsp;*&nbsp;Car&nbsp;乃「衍生自」(&quot;derived&nbsp;from&quot;)&nbsp;Vehicle&nbsp;<BR>&nbsp;*&nbsp;Car&nbsp;是个「特异化的」(&quot;a&nbsp;specialized&quot;)&nbsp;Vehicle&nbsp;<BR>&nbsp;*&nbsp;Car&nbsp;是&nbsp;Vehicle&nbsp;的「子类别」(&quot;subclass&quot;)&nbsp;<BR>&nbsp;*&nbsp;Vehicle&nbsp;是&nbsp;Car&nbsp;的「基底类别」(&quot;base&nbsp;class&quot;)&nbsp;<BR>&nbsp;*&nbsp;Vehicle&nbsp;是&nbsp;Car&nbsp;的「父类别」(&quot;superclass&quot;)&nbsp;(这不是&nbsp;C++&nbsp;界常用的说法)&nbsp;<BR>&nbsp;&nbsp;&nbsp;【译注】&quot;superclass&quot;&nbsp;是&nbsp;Smalltalk&nbsp;语言的关键字。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q53:把衍生类别的指标转型成指向它的基底,可以吗?&nbsp;<BR>&nbsp;<BR>可以。&nbsp;<BR>&nbsp;<BR>衍生类别是该基底类别的特异化版本(衍生者「是一种」(&quot;a-kind-of&quot;)&nbsp;基底)。这&nbsp;<BR>种向上的转换是绝对安全的,而且常常会发生(如果我指向一个汽车&nbsp;Car,实际上我&nbsp;<BR>是指向一个车子&nbsp;Vehicle):&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;f(Vehicle*&nbsp;v);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;g(Car*&nbsp;c)&nbsp;{&nbsp;f(c);&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//绝对很安全;不需要转型&nbsp;<BR>&nbsp;<BR>注意:在这里我们假设的是&nbsp;&quot;public&quot;&nbsp;的继承;後面会再提到「另一种」&quot;private/&nbsp;<BR>protected&quot;&nbsp;的继承。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q54:Derived*&nbsp;--&gt;&nbsp;Base*&nbsp;是正常的;那为什麽&nbsp;Derived**&nbsp;--&gt;&nbsp;Base**&nbsp;则否?&nbsp;<BR>&nbsp;<BR>C++&nbsp;让&nbsp;Derived*&nbsp;能转型到&nbsp;Base*,是因为衍生的物件「是一种」基底的物件。然而&nbsp;<BR>想由&nbsp;Derived**&nbsp;转型到&nbsp;Base**&nbsp;则是错误的!要是能够的话,Base**&nbsp;就可能会被解&nbsp;<BR>参用(产生一个&nbsp;Base*),该&nbsp;Base*&nbsp;就可能指向另一个“不一样的”衍生类别,这&nbsp;<BR>是不对的。&nbsp;<BR>&nbsp;<BR>照此看来,衍生类别的阵列就「不是一种」基底类别的阵列。在&nbsp;Paradigm&nbsp;Shift&nbsp;公&nbsp;<BR>司的&nbsp;C++&nbsp;训练课程里,我们用底下的例子来比喻:&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;一袋苹果「不是」一袋水果&quot;.&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;A&nbsp;bag&nbsp;of&nbsp;apples&nbsp;is&nbsp;NOT&nbsp;a&nbsp;bag&nbsp;of&nbsp;fruit&quot;.&nbsp;<BR>&nbsp;<BR>如果一袋苹果可以当成一袋水果来传递,别人就可能把香蕉放到苹果袋里头去!&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q55:衍生类别的阵列「不是」基底的阵列,是否表示阵列不好?&nbsp;<BR>&nbsp;<BR>没错,「阵列很烂」(开玩笑的&nbsp;:-)&nbsp;。&nbsp;<BR>&nbsp;<BR>C++&nbsp;内建的阵列有一个不易察觉的问题。想一想:&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;f(Base*&nbsp;arrayOfBase)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arrayOfBase[3].memberfn();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main()&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Derived&nbsp;arrayOfDerived[10];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f(arrayOfDerived);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR>&nbsp;<BR>编译器认为这完全是型别安全的,因为由&nbsp;Derived*&nbsp;转换到&nbsp;Base*&nbsp;是正常的。但事&nbsp;<BR>实上这很差劲:因为&nbsp;Derived&nbsp;可能会比&nbsp;Base&nbsp;还要大,f()&nbsp;里头的阵列索引不光是&nbsp;<BR>没有型别安全,甚至还可能没指到真正的物件呢!通常它会指到某个倒楣的&nbsp;<BR>Derived&nbsp;物件的中间去。&nbsp;<BR>&nbsp;<BR>根本的问题在於:C++&nbsp;不能分辨出「指向一个东西」和「指向一个阵列」。很自然的&nbsp;<BR>,这是&nbsp;C++“继承”自&nbsp;C&nbsp;语言的特徵。&nbsp;<BR>&nbsp;<BR>注意:如果我们用的是一个像阵列的「类别」而非最原始的阵列(譬如:&quot;Array&lt;T&gt;&quot;&nbsp;<BR>而非&nbsp;&quot;T[]&quot;),这问题就可以在编译期被挑出来,而非在执行的时候。&nbsp;<BR>&nbsp;<BR>==========================&nbsp;<BR>●&nbsp;12A:继承--虚拟函数&nbsp;<BR>==========================&nbsp;<BR>&nbsp;<BR>Q56:什麽是「虚拟成员函数」?&nbsp;<BR>&nbsp;<BR>虚拟函数可让衍生的类别「取代」原基底类别所提供的运作。只要某物件是衍生出来&nbsp;<BR>的,就算我们是透过基底物件的指标,而不是以衍生物件的指标来存取该物件,编译&nbsp;<BR>器仍会确保「取代後」的成员函数被呼叫。这可让基底类别的演算法被衍生者所替换&nbsp;<BR>,即使我们不知道衍生类别长什麽样子。&nbsp;<BR>&nbsp;<BR>注意:衍生的类别亦可“部份”取代(覆盖,override)掉基底的运作行为(如有必&nbsp;<BR>要,衍生类别的运作行为亦可呼叫它的基底类别版本)。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q57:C++&nbsp;怎样同时做到动态系结和静态型别?&nbsp;<BR>&nbsp;<BR>底下的讨论中,&quot;ptr&quot;&nbsp;指的是「指标」或「参考」。&nbsp;<BR>&nbsp;<BR>一个&nbsp;ptr&nbsp;有两种型态:静态的&nbsp;ptr&nbsp;型态,与动态的「被指向的物件」的型态(该物&nbsp;<BR>件可能实际上是个由其他类别衍生出来的类别的&nbsp;ptr)。&nbsp;<BR>&nbsp;<BR>「静态型别」(&quot;static&nbsp;typing&quot;)&nbsp;是指:该呼叫的「合法性」,是以&nbsp;ptr&nbsp;的静态型&nbsp;<BR>别为侦测之依据,如果&nbsp;ptr&nbsp;的型别能处理成员函数,则「指向的物件」自然也能。&nbsp;<BR>&nbsp;<BR>「动态系结」(&quot;dynamic&nbsp;binding&quot;)&nbsp;是指:「程式码」呼叫是以「被指向的物件」之&nbsp;<BR>型态为依据。被称为「动态系结」,是因为真正会被呼叫的程式码是动态地(於执行&nbsp;<BR>时期)决定的。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q58:衍生类别能否将基底类别的非虚拟函数覆盖(override)过去?&nbsp;<BR>&nbsp;<BR>可以,但不好。&nbsp;<BR>&nbsp;<BR>C++&nbsp;的老手有时会重新定义非虚拟的函数,以提升效率(换一种可能会运用到衍生类&nbsp;<BR>别才有的资源的作法),或是用以避开遮蔽效应(hiding&nbsp;rule,底下会提,或是看&nbsp;<BR>看&nbsp;ARM&nbsp;[&quot;Annotated&nbsp;Reference&nbsp;Manual&quot;]&nbsp;sect.13.1),但是用户的可见性效果必&nbsp;<BR>须完全相同,因为非虚拟的函数是以指标/参考的静态型别为分派(dispatch)的依&nbsp;<BR>据,而非以指到的/被参考到的物件之动态型别来决定。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q59:&quot;Warning:&nbsp;Derived::f(int)&nbsp;hides&nbsp;Base::f(float)&quot;&nbsp;是什麽意思?&nbsp;<BR>&nbsp;<BR>这是指:你死不了的。&nbsp;<BR>&nbsp;<BR>你出的问题是:如果&nbsp;Derived&nbsp;宣告了个叫做&nbsp;&quot;f&quot;&nbsp;的成员函数,Base&nbsp;却早已宣告了&nbsp;<BR>个不同型态签名型式(譬如:参数型态或是&nbsp;const&nbsp;不同)的&nbsp;&quot;f&quot;,这样子&nbsp;Base&nbsp;&quot;f&quot;&nbsp;<BR>就会被「遮蔽&nbsp;hide」住,而不是被「多载&nbsp;overload」或「覆盖&nbsp;override」(即使&nbsp;<BR>Base&nbsp;&quot;f&quot;&nbsp;已经是虚拟的了)。&nbsp;<BR>&nbsp;<BR>解决法:Derived&nbsp;要替&nbsp;Base&nbsp;被遮蔽的成员函数重新定义(就算它不是虚拟的)。通&nbsp;<BR>常重定义的函数,仅仅是去呼叫合适的&nbsp;Base&nbsp;成员函数,譬如:&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;Base&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;f(int);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;Derived&nbsp;:&nbsp;public&nbsp;Base&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;f(double);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;f(int&nbsp;i)&nbsp;{&nbsp;Base::f(i);&nbsp;}&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;^^^^^^^^^^---&nbsp;重定义的函数只是去呼叫&nbsp;Base::f(int)&nbsp;<BR>&nbsp;<BR>========================&nbsp;<BR>●&nbsp;12B:继承--一致性&nbsp;<BR>========================&nbsp;<BR>&nbsp;<BR>Q60:我该遮蔽住由基底类别继承来的公共成员函数吗?&nbsp;<BR>&nbsp;<BR>绝对绝对绝对绝对不要这样做!&nbsp;<BR>&nbsp;<BR>想去遮蔽(删去、撤消)掉继承下来的公共成员函数,是个很常见的错误。这通常是&nbsp;<BR>脑袋塞满了浆糊的人才会做的傻事。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q61:圆形&nbsp;&quot;Circle&quot;&nbsp;是一种椭圆&nbsp;&quot;Ellipse&quot;&nbsp;吗?&nbsp;<BR>&nbsp;<BR>若椭圆能够不对称地改变其两轴的大小,则答案就是否定的。&nbsp;<BR>&nbsp;<BR>比方说,椭圆有个&nbsp;&quot;setSize(x,y)&quot;&nbsp;的运作行为,且它保证说「椭圆的&nbsp;width()&nbsp;为&nbsp;<BR>x,height()&nbsp;为&nbsp;y」。这种情况之下,正圆形就不能算是一种椭圆。因为只要把某个&nbsp;<BR>椭圆能做而正圆形不能的东西放进去,圆形就不再是个椭圆了。&nbsp;<BR>&nbsp;<BR>这样一来,圆和椭圆之间可能有两种的(合法)关系:&nbsp;<BR>&nbsp;*&nbsp;将圆与椭圆完全分开来谈。&nbsp;<BR>&nbsp;*&nbsp;让圆及椭圆都同时自一个基底衍生出来,该基底为「不能做不对称的&nbsp;setSize&nbsp;<BR>&nbsp;&nbsp;&nbsp;运作的特殊椭圆形」。&nbsp;<BR>&nbsp;<BR>以第一个方案而言,椭圆可继承自「非对称图形」(伴随著一个&nbsp;setSize(x,y)&nbsp;),&nbsp;<BR>圆形则继承自「对称图形」,带有一个&nbsp;setSize(size)&nbsp;成员函数。&nbsp;<BR>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -