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

📄 00000002.htm

📁 一份很好的linux入门资料
💻 HTM
📖 第 1 页 / 共 3 页
字号:
&nbsp;<BR>虚拟资料让衍生类别能改变基底类别的物件成员所属的类别。严格说来,C++&nbsp;并不「&nbsp;<BR>支援」虚拟资料,但可以模拟出来。不漂亮,但还能用。&nbsp;<BR>&nbsp;<BR>欲模拟之,基底类别必须有个指标指向成员物件,衍生类别必须提供一个&nbsp;&quot;new&quot;&nbsp;到&nbsp;<BR>的物件,以让原基底类别的指标所指到。该基底类别也要有一个以上正常的建构子,&nbsp;<BR>以提供它们自己的参考(也是透过&nbsp;&quot;new&quot;),且基底类别的解构子也要&nbsp;&quot;delete&quot;&nbsp;掉&nbsp;<BR>被参考者。&nbsp;<BR>&nbsp;<BR>举例来说,&quot;Stack&quot;&nbsp;类别可能有个&nbsp;Array&nbsp;成员物件(采用指标),衍生类别&nbsp;<BR>&quot;StretchableStack&quot;&nbsp;可能会把基底类别的成员资料&nbsp;&quot;Array&quot;&nbsp;覆盖成&nbsp;<BR>&quot;StretchableArray&quot;。想做到的话,StretchableArray&nbsp;必须继承自&nbsp;Array,这样子&nbsp;<BR>Stack&nbsp;就会有个&nbsp;&quot;Array*&quot;。Stack&nbsp;的正常建构子会用&nbsp;&quot;new&nbsp;Array&quot;&nbsp;来初始化它的&nbsp;<BR>&quot;Array*&quot;,但&nbsp;Stack&nbsp;也会有一个(可能是在&nbsp;&quot;protected:&quot;&nbsp;里)特别的建构子,以&nbsp;<BR>自衍生类别中接收一个&nbsp;&quot;Array*&quot;;&nbsp;StretchableArray&nbsp;的建构子会用&nbsp;<BR>&quot;new&nbsp;StretchableArray&quot;&nbsp;把它传给那个特别的建构子。&nbsp;<BR>&nbsp;<BR>优点:&nbsp;<BR>&nbsp;*&nbsp;容易做出&nbsp;StretchableStack(大部份的程式都继承下来了)。&nbsp;<BR>&nbsp;*&nbsp;使用者可把&nbsp;StretchableStack&nbsp;当成“是一种”Stack&nbsp;来传递。&nbsp;<BR>&nbsp;<BR>缺点:&nbsp;<BR>&nbsp;*&nbsp;多增加额外的间接存取层,才能碰到&nbsp;Array。&nbsp;<BR>&nbsp;*&nbsp;多增加额外的自由记忆体配置负担(new&nbsp;与&nbsp;delete)。&nbsp;<BR>&nbsp;*&nbsp;多增加额外的动态系结负担(原因请见下一则&nbsp;FAQ)。&nbsp;<BR>&nbsp;<BR>换句话说,在“我们”这一边,很轻松就成功做出&nbsp;StretchableStack,但所有用户&nbsp;<BR>却都为此付出代价。不幸的,额外负荷不仅在&nbsp;StretchableStack&nbsp;会有,连&nbsp;Stack&nbsp;<BR>也会。&nbsp;<BR>&nbsp;<BR>请看下下一则&nbsp;FAQ,看看使用者会「付出」多少代价。也请读读下一则&nbsp;FAQ&nbsp;以後的&nbsp;<BR>几则(不看其他的,你将得不到平衡的报导)。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q99:虚拟资料和动态资料有何差别?&nbsp;<BR>&nbsp;<BR>最容易分辨出来的方法,就是看看颇为类似的「虚拟函数」。虚拟成员函数是指:在&nbsp;<BR>所有子类别中,它的宣告(型态签名)部份必须保持不变,但是定义(本体)的部份&nbsp;<BR>可以被覆盖(override)。继承下来的成员函数可被覆盖,是子类别的静态性质&nbsp;<BR>(static&nbsp;property);它不随任何物件之生命期而动态地改变,同一个子类别的不同&nbsp;<BR>物件,也不可能会有不同的成员函数的定义。&nbsp;<BR>&nbsp;<BR>现在请回头重读前面这一段,但稍作些代换:&nbsp;<BR>&nbsp;*&nbsp;「成员函数」&nbsp;--&gt;&nbsp;「成员物件」&nbsp;<BR>&nbsp;*&nbsp;「型态签名」&nbsp;--&gt;&nbsp;「型别」&nbsp;<BR>&nbsp;*&nbsp;「本体」&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&gt;&nbsp;「真正所属的类别」&nbsp;<BR>这样子,你就看到虚拟资料的定义。&nbsp;<BR>&nbsp;<BR>从另一个角度来看,就是把「各个物件」的成员函数与「动态」成员函数区分开来。&nbsp;<BR>「各个物件」成员函数是指:在任何物件案例中,该成员函数可能会有所不同,可能&nbsp;<BR>会塞入函数指标来实作出来;这个指标可以是&nbsp;&quot;const&quot;,因为它在物件生命期中不会&nbsp;<BR>变更。「动态」成员函数是指:该成员函数会随时间动态地改变;也可能以函数指标&nbsp;<BR>来做,但该指标不会是&nbsp;const&nbsp;的。&nbsp;<BR>&nbsp;<BR>推而广之,我们得到三种不同的资料成员概念:&nbsp;<BR>&nbsp;*&nbsp;虚拟资料:成员物件的定义(真正所属的类别)可被子类别覆盖,只要它的宣告&nbsp;<BR>&nbsp;&nbsp;&nbsp;(型别)维持不变,且此覆盖是子类别的静态性质。&nbsp;<BR>&nbsp;*&nbsp;各物件的资料:任何类别的物件在初始化时,可以案例化不同型式(型别)的成&nbsp;<BR>&nbsp;&nbsp;&nbsp;员物件(通常是一个&nbsp;&quot;wrapper&quot;&nbsp;包起来的物件),且该成员物件真正所属的类别&nbsp;<BR>&nbsp;&nbsp;&nbsp;,是把它包起来的那个物件之静态性质。&nbsp;<BR>&nbsp;*&nbsp;动态资料:成员物件真正所属的类别,可随时间动态地改变。&nbsp;<BR>&nbsp;<BR>它们看起来都差不多,是因为&nbsp;C++&nbsp;都不「直接支援」它们,只是「能做得出来」而&nbsp;<BR>已;在这种情形下,模拟它们的机制也都一样:指向(可能是抽象的)基底类别的指&nbsp;<BR>标。在直接提供这些&nbsp;&quot;first&nbsp;class&quot;&nbsp;抽象化机制的语言中,这三者间的差别十分明&nbsp;<BR>显,它们各有不同的语法。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q100:我该正常地用指标来配置资料成员,还是该用「成份」(composition)?&nbsp;<BR>&nbsp;<BR>成份。&nbsp;<BR>&nbsp;<BR>正常情况下,你的成员资料应该被「包含」在合成的物件里(但也不总是如此;&nbsp;<BR>&quot;wrapper&quot;&nbsp;物件就是你会想用指标/参考的好例子;N-to-1-uses-a&nbsp;的关系也需要某&nbsp;<BR>种指标/参考之类的东西)。&nbsp;<BR>&nbsp;<BR>有三个理由说明,完全被包含的成员物件(「成份」)的效率,比自由配置物件的指&nbsp;<BR>标还要好:&nbsp;<BR>&nbsp;<BR>&nbsp;*&nbsp;额外的间接层,每当你想存取成员物件时。&nbsp;<BR>&nbsp;*&nbsp;额外的动态配置(&quot;new&quot;&nbsp;於建构子中,&quot;delete&quot;&nbsp;於解构子中)。&nbsp;<BR>&nbsp;*&nbsp;额外的动态系结(底下会解释)。&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q101:动态配置成员物件有三个效率因素,它们的相对代价是多少?&nbsp;<BR>&nbsp;<BR>这三个效率因素,上一则&nbsp;FAQ&nbsp;有列举出来:&nbsp;<BR>&nbsp;*&nbsp;以它本身而言,额外的间接层影响不大。&nbsp;<BR>&nbsp;*&nbsp;动态配置可能是个效率问题(当有许多配置动作时,典型的&nbsp;malloc&nbsp;会拖慢速度&nbsp;<BR>&nbsp;&nbsp;&nbsp;;OO&nbsp;软体会被动态配置拖垮,除非你事先就留意到它了)。&nbsp;<BR>&nbsp;*&nbsp;用指标而非物件的话,会带来额外的动态系结。每当&nbsp;C++&nbsp;编译器能知道某物件「&nbsp;<BR>&nbsp;&nbsp;&nbsp;真正的」类别,该虚拟函数呼叫就能“静态”地系结住,能够被&nbsp;inline。Inline&nbsp;<BR>&nbsp;&nbsp;&nbsp;可能有无限大的&nbsp;(但你可能只会相信有半打的&nbsp;:-)&nbsp;最佳化机会,像是&nbsp;procedural&nbsp;<BR>&nbsp;&nbsp;&nbsp;integration、暂存器生命期等等事项。三种情形之下,C++&nbsp;编译器能知道物件真&nbsp;<BR>&nbsp;&nbsp;&nbsp;正的类别:区域变数、整体/静态变数、完全被包含的成员物件。&nbsp;<BR>&nbsp;<BR>完全被包含的成员物件,可达到很大的最佳化效果,这是「成员物件的指标」所不可&nbsp;<BR>能办到的。这也就是为什麽采用参考语意的语言,会「与生俱来」就效率不彰的原因&nbsp;<BR>了。&nbsp;<BR>&nbsp;<BR>注意:请读读下面三则&nbsp;FAQs&nbsp;以得到平衡的观点!&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q102:&quot;inline&nbsp;virtual&quot;&nbsp;的成员函数真的会被&nbsp;&quot;inline&quot;&nbsp;吗?&nbsp;<BR>&nbsp;<BR>Yes,可是...&nbsp;<BR>&nbsp;<BR>一个透过指标或参考的&nbsp;virtual&nbsp;呼叫总是动态决定的,可能永远都不会被&nbsp;inline。&nbsp;<BR>原因:编译器直到执行时(亦即:动态地),才会知道该呼叫哪个程式,因为那一段&nbsp;<BR>程式,可能会来自呼叫者编译过後才出现的衍生类别。&nbsp;<BR>&nbsp;<BR>因此,inline&nbsp;virtual&nbsp;的呼叫可被&nbsp;inline&nbsp;的唯一时机是:编译器有办法知道某物&nbsp;<BR>件「真正所属的类别」之时,这是虚拟函数呼叫里所要知道的事情。这只会发生在:&nbsp;<BR>编译器拥有真正的物件,而非该物件的指标或参考。也就是说:不是区域变数、整体&nbsp;<BR>/静态物件,就是合成物件里的完全包含物件。&nbsp;<BR>&nbsp;<BR>注意:inline&nbsp;和非&nbsp;inline&nbsp;的差距,通常会比正常的和虚拟的函数呼叫之差别更为&nbsp;<BR>显著。譬如,正常的与虚拟的函数呼叫,通常只差两个记忆体参考的动作而已,可是&nbsp;<BR>inline&nbsp;与非&nbsp;inline&nbsp;函数就会有一个数量级的差距(与数万次影响不大的成员函数&nbsp;<BR>呼叫相比,函数没有用&nbsp;inline&nbsp;virtual&nbsp;的话,会造成&nbsp;25X&nbsp;的效率损失!&nbsp;<BR>[Doug&nbsp;Lea,&nbsp;&quot;Customization&nbsp;in&nbsp;C++,&quot;&nbsp;proc&nbsp;Usenix&nbsp;C++&nbsp;1990]).&nbsp;<BR>&nbsp;<BR>针对此现象的对策:不要陷入编译器/语言厂商之间,对彼此产品的虚拟函数呼叫,&nbsp;<BR>做永无止尽的性能比较争论(或是广告噱头!)之中。和语言/编译器能否将成员函&nbsp;<BR>数呼叫做「行内展开」相比,这种比较完全没有意义。也就是说,许多语言编译器厂&nbsp;<BR>商,拼命强调他们的函数分派方式有多好,但如果他们没做“行内”成员函数呼叫的&nbsp;<BR>话,整体性能还是会很差,因为&nbsp;inline--而非分派--才是最重要的性能影响因&nbsp;<BR>素。&nbsp;<BR>&nbsp;<BR>注意:请读读下两则&nbsp;FAQs&nbsp;以看看另一种说法!&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q103:看起来我不应该用参考语意了,是吗?&nbsp;<BR>&nbsp;<BR>错。&nbsp;<BR>&nbsp;<BR>参考语意是个好东西。我们不能抛弃指标,我们只要不让软体的指标变成一个大老鼠&nbsp;<BR>窝就行了。在&nbsp;C++&nbsp;里,你可以选择该用参考语意(指标/参考)还是数值语意(物&nbsp;<BR>件真正包含其他物件)的时机。在大型系统中,两者应该取得平衡。然而如果你全都&nbsp;<BR>用指标来做的话,速度会大大的降低。&nbsp;<BR>&nbsp;<BR>接近问题层次的物件,会比较高阶的物件还要大。这些针对「问题空间」抽象化的个&nbsp;<BR>体本身,通常比它们内部的「数值」更为重要。参考语意应该用於问题空间的物件上&nbsp;<BR>。&nbsp;<BR>&nbsp;<BR>注意:问题空间的物件,通常会比解题空间的更为高阶抽象化,所以相对地问题空间&nbsp;<BR>的物件通常会有较少的交谈性。因此&nbsp;C++&nbsp;给我们一个“理想的”解决法:我们用参&nbsp;<BR>考语意,来对付那些需要独立的个体识别&nbsp;(identity)&nbsp;者,或是大到不适合直接拷贝&nbsp;<BR>的物件;其他情形则可选择数值语意。因此,使用频率较高的就用数值语意,因为(&nbsp;<BR>只有)在不造成伤害的场合下,我们才去增加弹性;必要时,我们还是选择效率!&nbsp;<BR>&nbsp;<BR>还有其他关於实际&nbsp;OO&nbsp;设计方面的问题。想精通&nbsp;OO/C++&nbsp;得花时间,以及高素质的训&nbsp;<BR>练。若你想有个强大的工具,你必须投资下去。&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;&lt;&lt;&lt;&nbsp;还不要停下来!&nbsp;&nbsp;请一并读读下一则&nbsp;FAQ!!&nbsp;&gt;&gt;&gt;&gt;&nbsp;<BR>&nbsp;<BR>========================================&nbsp;<BR>&nbsp;<BR>Q104:参考语意效率不高,那麽我是否应该用传值呼叫?&nbsp;<BR>&nbsp;<BR>不。&nbsp;<BR>&nbsp;<BR>前面的&nbsp;FAQ&nbsp;是讨论“成员物件”(member&nbsp;object)&nbsp;的,而不是函数参数。一般说来&nbsp;<BR>,位於继承阶层里的物件,应该用参考或指标来传递,而非传值,因为惟有如此你才&nbsp;<BR>能得到(你想要的)动态系结(传值呼叫和继承不能安全混用,因为如果把大大的子&nbsp;<BR>类别物件当成基底的物件来传值的话,它会被“切掉”)。&nbsp;<BR>&nbsp;<BR>除非有足以令人信服的反方理由,否则成员物件应该用数值,而参数该用参考传递。&nbsp;<BR>前几则&nbsp;FAQs&nbsp;提到一些「足以信服的理由」,以支持“成员物件该用参考”一事了。&nbsp;<BR>&nbsp;<BR>--&nbsp;<BR>Marshall&nbsp;Cline&nbsp;<BR>--&nbsp;<BR>Marshall&nbsp;P.&nbsp;Cline,&nbsp;Ph.D.&nbsp;/&nbsp;Paradigm&nbsp;Shift&nbsp;Inc&nbsp;/&nbsp;PO&nbsp;Box&nbsp;5108&nbsp;/&nbsp;Potsdam&nbsp;NY&nbsp;13676&nbsp;<BR><A HREF="mailto:cline@sun.soe.clarkson.edu">cline@sun.soe.clarkson.edu</A>&nbsp;/&nbsp;315-353-6100&nbsp;/&nbsp;FAX:&nbsp;315-353-6110&nbsp;<BR>&nbsp;<BR>&nbsp;<BR><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER></BODY></HTML>

⌨️ 快捷键说明

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