• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            天行健 君子當(dāng)自強(qiáng)而不息

            【ZT】C++批判(1)

            以下文章翻譯自Ian Joyner所著的
            《C++?? A Critique of C++ and Programming and Language Trends of the 1990s》 3/E【Ian Joyner 1996】

            該篇文章已經(jīng)包含在Ian Joyner所寫(xiě)的《Objects Unencapsulated 》一書(shū)中(目前已經(jīng)有了日文的翻譯版本),該書(shū)的介紹可參見(jiàn)于:
            http://www.prenhall.com/allbooks/ptr_0130142697.html
            http://efsa.sourceforge.net/cgi-bin/view/Main/ObjectsUnencapsulated
            http://www.accu.org/bookreviews/public/reviews/o/o002284.htm


            虛擬函數(shù)

              在所有對(duì)C++的批評(píng)中,虛擬函數(shù)這一部分是最復(fù)雜的。這主要是由于C++中復(fù)雜的機(jī)制所引起的。雖然本篇文章認(rèn)為多態(tài)(polymorphism)是實(shí)現(xiàn)面向?qū)ο缶幊蹋∣OP)的關(guān)鍵特性,但還是請(qǐng)你不要對(duì)此觀點(diǎn)(即虛擬函數(shù)機(jī)制是C++中的一大敗筆)感到有什么不安,繼續(xù)看下去,如果你僅僅想知道一個(gè)大概的話,那么你也可以跳過(guò)此節(jié)。【譯者注:建議大家還是看看這節(jié)會(huì)比較好】

             在C++中,當(dāng)子類(lèi)改寫(xiě)/重定義(override/redefine)了在父類(lèi)中定義了的函數(shù)時(shí),關(guān)鍵字virtual使得該函數(shù)具有了多態(tài)性,但是 virtual關(guān)鍵字也并不是必不可少的(只要在父類(lèi)中被定義一次就行了)。編譯器通過(guò)產(chǎn)生動(dòng)態(tài)分配(dynamic dispatch)的方式來(lái)實(shí)現(xiàn)真正的多態(tài)函數(shù)調(diào)用。

              這樣,在C++中,問(wèn)題就產(chǎn)生了:如果設(shè)計(jì)父類(lèi)的人員不能預(yù)見(jiàn)到子類(lèi)可能會(huì)改寫(xiě)哪個(gè)函數(shù),那么子類(lèi)就不能使得這個(gè)函數(shù)具有多態(tài)性。這對(duì)于C++來(lái)說(shuō)是一個(gè)很?chē)?yán)重的缺陷,因?yàn)樗鼫p少了軟件組件(software components)的彈性(flexibility),從而使得寫(xiě)出可重用及可擴(kuò)展的函數(shù)庫(kù)也變得困難起來(lái)。

             C++同時(shí)也允許函數(shù)的重載(overload),在這種情況下,編譯器通過(guò)傳入的參數(shù)來(lái)進(jìn)行正確的函數(shù)調(diào)用。在函數(shù)調(diào)用時(shí)所引用的實(shí)參類(lèi)型必須吻合被重載的函數(shù)組(overloaded functions)中某一個(gè)函數(shù)的形參類(lèi)型。重載函數(shù)與重寫(xiě)函數(shù)(具有多態(tài)性的函數(shù))的不同之處在于:重載函數(shù)的調(diào)用是在編譯期間就被決定了,而重寫(xiě)函數(shù)的調(diào)用則是在運(yùn)行期間被決定的。

             當(dāng)一個(gè)父類(lèi)被設(shè)計(jì)出來(lái)時(shí),程序員只能猜測(cè)子類(lèi)可能會(huì)重載/重寫(xiě)哪個(gè)函數(shù)。子類(lèi)可以隨時(shí)重載任何一個(gè)函數(shù),但這種機(jī)制并不是多態(tài)。為了實(shí)現(xiàn)多態(tài),設(shè)計(jì)父類(lèi)的程序員必須指定一個(gè)函數(shù)為virtual,這樣會(huì)告訴編譯器在類(lèi)的跳轉(zhuǎn)表(class jump table)【譯者竊以為是vtable,即虛擬函數(shù)入口表】中建立一個(gè)分發(fā)入口。于是,對(duì)于決定什么事情是由編譯器自動(dòng)完成,或是由其他語(yǔ)言的編譯器自動(dòng)完成這個(gè)重任就放到了程序員的肩上。這些都是從最初的C++的實(shí)現(xiàn)中繼承下來(lái)的,而和一些特定的編譯器及聯(lián)結(jié)器無(wú)關(guān)。

             對(duì)于重寫(xiě),我們有著三種不同的選擇,分別對(duì)應(yīng)于:“千萬(wàn)別”,“可以”及“一定要”重寫(xiě):

             1、重寫(xiě)一個(gè)函數(shù)是被禁止的。子類(lèi)必須使用已有的函數(shù)。
             2、函數(shù)可以被重寫(xiě)。子類(lèi)可以使用已有的函數(shù),也可以使用自己寫(xiě)的函數(shù),前提是這個(gè)函數(shù)必須遵循最初的界面定義,而且實(shí)現(xiàn)的功能盡可能的少及完善。
             3、函數(shù)是一個(gè)抽象的函數(shù)。對(duì)于該函數(shù)沒(méi)有提供任何的實(shí)現(xiàn),每個(gè)子類(lèi)都必須提供其各自的實(shí)現(xiàn)。
             
             父類(lèi)的設(shè)計(jì)者必須要決定1和3中的函數(shù),而子類(lèi)的設(shè)計(jì)者只需要考慮2就行了。對(duì)于這些選擇,程序語(yǔ)言必須要提供直接的語(yǔ)法支持。
             
            選項(xiàng)1:
             
             C ++并不能禁止在子類(lèi)中重寫(xiě)一個(gè)函數(shù)。即使是被聲明為private virtual的函數(shù)也可以被重寫(xiě)。【Sakkinen92】中指出了即使在通過(guò)其他方法都不能訪問(wèn)到private virtual函數(shù),子類(lèi)也可以對(duì)其進(jìn)行重寫(xiě)。

            如下所示,將輸出:class B

            #include <stdio.h>

            class A
            {
            private:
                virtual void f()
                {
                    printf("class A\n");
                }

            public:
                void call_f()
                {
                    f();
                }
            };

            class B : public A
            {
            public:
                void f()
                {
                    printf("class B\n");
                }
            };


            int main()
            {
                B b;
                A* a = &b;

                a->call_f();

                return 0;
            }


            實(shí)現(xiàn)這種選擇的唯一方法就是不要使用虛擬函數(shù),但是這樣的話,函數(shù)就等于整個(gè)被替換掉了。首先,函數(shù)可能會(huì)在無(wú)意中被子類(lèi)的函數(shù)給替換掉。在同一個(gè)scope中重新宣告一個(gè)函數(shù)將會(huì)導(dǎo)致名字沖突(name clash);編譯器將會(huì)就此報(bào)告出一個(gè)“duplicate declaration”的語(yǔ)法錯(cuò)誤。允許兩個(gè)擁有同名的實(shí)體存在于同一個(gè)scope中將會(huì)導(dǎo)致語(yǔ)義的二義性(ambiguity)及其他問(wèn)題(可參見(jiàn)于 name overloading這節(jié))。
             
             下面的例子闡明了第二個(gè)問(wèn)題:

             class A
             {
              public:
                   void nonvirt();
                   virtual void virt();
             };

             class B : public A
             {
              public:
                   void nonvirt();
                   void virt();
             };
             
             A a;
             B b;
             A *ap = &b;
             B *bp = &b;
             
             bp->nonvirt(); file://calls B::nonvirt as you would eXPect
             ap->nonvirt(); file://calls A::nonvirt even though this object is of type B
             ap->virt();  file://calls B::virt, the correct version of the routine for B objects

             
            在這個(gè)例子里,B擴(kuò)展或替換掉了A中的函數(shù)。B::nonvirt是應(yīng)該被B的對(duì)象調(diào)用的函數(shù)。在此處我們必須指出,C++給客戶端程序員(即使用我們這套繼承體系架構(gòu)的程序員)足夠的彈性來(lái)調(diào)用A::nonvirt或是B::nonvirt,但我們也可以提供一種更簡(jiǎn)單,更直接的方式:提供給A:: nonvirt和B::nonvirt不同的名字。這可以使得程序員能夠正確地,顯式地調(diào)用想要c調(diào)用的函數(shù),而

            不是陷入了上面的那種晦澀的,容易導(dǎo)致錯(cuò)誤的陷阱中去。

            具體方法如下:

             class B:  public A
             {
              public:
                   void b_nonvirt();
                   void virt();
             }

             B b;
             B *bp = &b;

            bp->nonvirt();  file://calls A::nonvirt
            bp->b_nonvirt(); file://calls B::b_nonvirt

             
              現(xiàn)在,B的設(shè)計(jì)者就可以直接的操縱B的接口了。程序要求B的客戶端(即調(diào)用B的代碼)能夠同時(shí)調(diào)用A::nonvirt和B::nonvirt,這點(diǎn)我們也做到了。就Object-Oriented Design(OOD)來(lái)說(shuō),這是一個(gè)不錯(cuò)的做法,因?yàn)樗峁┝私训慕涌诙x(strongly defined interface)【譯者認(rèn)為:即不會(huì)引起調(diào)用歧義的接口】。C++允許客戶端程序員在類(lèi)的接口處賣(mài)弄他們的技巧,借以對(duì)類(lèi)進(jìn)行擴(kuò)展。在上例中所出現(xiàn)的就是設(shè)計(jì)B的程序員不能阻止其他程序員調(diào)用A::nonvirt。類(lèi)B的對(duì)象擁有它們自己的nonvirt,但是即便如此,B的設(shè)計(jì)者也不能保證通過(guò)B的接口就一定能調(diào)用到正確版本的nonvirt。
             
             C++同樣不能阻止系統(tǒng)中對(duì)其他處的改動(dòng)不會(huì)影響到B。假設(shè)我們需要寫(xiě)一個(gè)類(lèi)C,在C 中我們要求nonvirt是一個(gè)虛擬的函數(shù)。于是我們就必須回到A中將nonvirt改為虛擬的。但這又將使得我們對(duì)于B::nonvirt所玩弄的技巧又失去了作用(想想看,為什么:D)。對(duì)于C需要一個(gè)virtual的需求(將已有的nonvirtual改為virtual)使得我們改變了父類(lèi),這又使得所有從父類(lèi)繼承下來(lái)的子類(lèi)也相應(yīng)地有了改變。這已經(jīng)違背了OOP擁有低耦合的類(lèi)的理由,新的需求,改動(dòng)應(yīng)該只產(chǎn)生局部的影響,而不是改變系統(tǒng)中其他地方,從而潛在地破壞了系統(tǒng)的已有部分。
             
             另一個(gè)問(wèn)題是,同樣的一條語(yǔ)句必須一直保持著同樣的語(yǔ)義。例如:對(duì)于諸如a->f()這樣的多態(tài)性語(yǔ)句的解釋?zhuān)到y(tǒng)調(diào)用的是由最符合a所真正指向類(lèi)型的那個(gè)f(),而不管對(duì)象的類(lèi)型到底是A,還是A的子類(lèi)。然而,對(duì)于C++的程序員來(lái)說(shuō),他們必須要清楚地了解當(dāng)f()被定義成virtual或是non-virtual時(shí),a->f()的真正涵義。所以,語(yǔ)句a->f()不能獨(dú)立于其實(shí)現(xiàn),而且隱藏的實(shí)現(xiàn)原理也不是一成不變的。對(duì)于f()聲明的一次改變將會(huì)相應(yīng)地改變調(diào)用它時(shí)的語(yǔ)義。與實(shí)現(xiàn)獨(dú)立意味著對(duì)于實(shí)現(xiàn)的改變不會(huì)改變語(yǔ)句的語(yǔ)義,或是執(zhí)行的語(yǔ)義。
             
             如果在聲明中的改變導(dǎo)致相應(yīng)的語(yǔ)義改變,編譯器應(yīng)該能檢測(cè)到錯(cuò)誤的產(chǎn)生。程序員應(yīng)該在聲明被改變的情況下保持語(yǔ)義的不變。這反映了軟件開(kāi)發(fā)中的動(dòng)態(tài)特性,在其中你將能發(fā)現(xiàn)程序文本的永久改變。
             
             其他另一個(gè)與a->f()相應(yīng)的,語(yǔ)義不能被保持不變的例子是:構(gòu)造函數(shù)(可參考于C++ ARM, section 10.9c, p 232)。而Eiffel和Java則不存在這樣的問(wèn)題。它們中所采用的機(jī)制簡(jiǎn)單而又清晰,不會(huì)導(dǎo)致C++中所產(chǎn)生的那些令人吃驚的現(xiàn)象。在Java中,所有的方法都是虛擬的,為了讓一個(gè)方法【譯者注:對(duì)應(yīng)于C++的函數(shù)】不能被重寫(xiě),我們可以用final修飾符來(lái)修飾這個(gè)方法。
             
             Eiffel允許程序員指定一個(gè)函數(shù)為frozen,在這種情況下,這個(gè)函數(shù)就不能在子類(lèi)中被重寫(xiě)。
             
            選項(xiàng)2:

              是使用現(xiàn)有的函數(shù)還是重寫(xiě)一個(gè),這應(yīng)該是由撰寫(xiě)子類(lèi)的程序員所決定的。在C++中,要想擁有這種能力則必須在父類(lèi)中指定為virtual。對(duì)于OOD來(lái)說(shuō),你所決定不想作的與你所決定想作的同樣重要,你的決定應(yīng)該是越遲下越好。這種策略可以避免錯(cuò)誤在系統(tǒng)前期就被包含進(jìn)去。你作決定越早,你就越有可能被以后所證明是錯(cuò)誤的假設(shè)所包圍;或是你所作的假設(shè)在一種情況下是正確的,然而在另一種情況下卻會(huì)出錯(cuò),從而使得你所寫(xiě)出來(lái)的軟件比較脆弱,不具有重用性(reusable)【譯者注:軟件的可重用性對(duì)于軟件來(lái)說(shuō)是一個(gè)很重要的特性,具體可以參考
            《Object-Oriented Software Construct》中對(duì)于軟件的外部特性的敘述,P7, Reusability, Charpter 1.2 A REVIEW OF EXTERNAL FACTORS】。
             
             C ++要求我們?cè)诟割?lèi)中就要指定可能的多態(tài)性(這可以通過(guò)virtual來(lái)指定),當(dāng)然我們也可以在繼承鏈中的中間的類(lèi)導(dǎo)入virtual機(jī)制,從而預(yù)先判斷某個(gè)函數(shù)是否可以在子類(lèi)中被重定義。

            這種做法將導(dǎo)致問(wèn)題的出現(xiàn):如那些并非真正多態(tài)的函數(shù)(not actually polymorphic)也必須通過(guò)效率較低的table技術(shù)來(lái)被調(diào)用,而不像直接調(diào)用那個(gè)函數(shù)來(lái)的高效【譯者注:在文章的上下文中并沒(méi)有出現(xiàn)not actually polymorphic特性的確切定義,根據(jù)我的理解,應(yīng)該是聲明為polymorphic,而實(shí)際上的動(dòng)作并沒(méi)能體現(xiàn)polymorphic這樣的一種特性】。雖然這樣做并不會(huì)引起大量的花費(fèi)(overhead),但我們知道,在OO程序中經(jīng)常會(huì)出現(xiàn)使用大量的、短小的、目標(biāo)單一明確的函數(shù),如果將所有這些都累計(jì)下來(lái),也會(huì)導(dǎo)致一個(gè)相當(dāng)可觀的花費(fèi)。C++中的

            政策是這樣的:需要被重定義的函數(shù)必須被聲明為virtual。糟糕的是,C++同時(shí)也說(shuō)了, non-virtual函數(shù)不能被重定義,這使得設(shè)計(jì)使用子類(lèi)的程序員就無(wú)法對(duì)于這些函數(shù)擁有自己的控制權(quán)。【譯者注:原作中此句顯得有待推敲,原文是這樣寫(xiě)的:it says that non-virtual routines cannot be redefined, 我猜測(cè)作者想表達(dá)的意思應(yīng)該是:If you have defined a non-virtual routine in base, then it cannot be virtual in the base whether you redefined it as virtual in descendant.】

            Rumbaugh等人對(duì)于C++中的虛擬機(jī)制的批評(píng)如下:C++擁有了簡(jiǎn)單實(shí)現(xiàn)繼承及動(dòng)態(tài)方法調(diào)用的特性,但一個(gè)C++的數(shù)據(jù)結(jié)構(gòu)并不能自動(dòng)成為面向?qū)ο蟮摹7椒ㄕ{(diào)用決議(method resolution)以及在子類(lèi)中重寫(xiě)一個(gè)函數(shù)操作的前提必須是這個(gè)函數(shù)/方法已經(jīng)在父類(lèi)中被聲明為virtual。也就是說(shuō),必須在最初的類(lèi)中我們就能預(yù)見(jiàn)到一個(gè)函數(shù)是否需要被重寫(xiě)。不幸的是,類(lèi)的撰寫(xiě)者可能不會(huì)預(yù)期到需要定義一個(gè)特殊的子類(lèi),也可能不會(huì)知道那些操作將要在子類(lèi)中被重寫(xiě)。這意味著當(dāng)子類(lèi)被定義時(shí),我們經(jīng)常需要回過(guò)頭去修改我們的父類(lèi),并且使得對(duì)于通過(guò)創(chuàng)建子類(lèi)來(lái)重用已有的庫(kù)的限制極為嚴(yán)格,尤其是當(dāng)這個(gè)庫(kù)的源代碼不能被獲得是更是如此。(當(dāng)然,你也可以將所有的操作都定義為virtual,并愿意為此付出一些小小的內(nèi)存花費(fèi)用于函數(shù)調(diào)用)【RBPEL91】
             
             然而,讓程序員來(lái)處理virtual是一個(gè)錯(cuò)誤的機(jī)制。編譯器應(yīng)該能夠檢測(cè)到多態(tài),并為此產(chǎn)生所必須的、潛在的實(shí)現(xiàn)virtual的代碼。讓程序員來(lái)決定 virtual與否對(duì)于程序員來(lái)說(shuō)是增加了一個(gè)簿記工作的負(fù)擔(dān)。這也就是為什么C++只能算是一種弱的面向?qū)ο笳Z(yǔ)言(weak object-oriented language):因?yàn)槌绦騿T必須時(shí)刻注意著一些底層的細(xì)節(jié)(low level details),而這些本來(lái)可以由編譯器自動(dòng)處理的。
             
             在C++中的另一個(gè)問(wèn)題是錯(cuò)誤的重寫(xiě)(mistaken overriding),父類(lèi)中的函數(shù)可以在毫不知情的情況下被重寫(xiě)。編譯器應(yīng)該對(duì)于同一個(gè)名字空間中的重定義報(bào)錯(cuò),除非編寫(xiě)子類(lèi)的程序員指出他是有意這么做的(即對(duì)于虛函數(shù)的重寫(xiě))。我們可以使用同一個(gè)名字,但是程序員必須清楚自己在干什么,并且顯式地聲明它,尤其是在將自己的程序與已經(jīng)存在的程序組件組裝成新的系統(tǒng)的情況下更要如此。除非程序員顯式地重寫(xiě)已有的虛函數(shù),否則編譯器必須要給我們報(bào)告出現(xiàn)了名字被聲明多處(duplicate declaration)的錯(cuò)誤。然而,C++卻采用了Simula最初的做法,而這種方法到現(xiàn)在已經(jīng)得到了改良。其他的一些程序語(yǔ)言通過(guò)采用了更好的、更加顯式的方法,避免了錯(cuò)誤重定義的出現(xiàn)。
             
             解決方法就是virtual不應(yīng)該在父類(lèi)中就被指定好。當(dāng)我們需要運(yùn)行時(shí)的動(dòng)態(tài)綁定時(shí),我們就在子類(lèi)中指定需要對(duì)某個(gè)函數(shù)進(jìn)行重寫(xiě)。這樣做的好處在于:對(duì)于具有多態(tài)性的函數(shù),編譯器可以檢測(cè)其函數(shù)簽名(function signature)的一致性;而對(duì)于重載的函數(shù),其函數(shù)簽名在某些方面本來(lái)就不一樣。第二個(gè)好處表現(xiàn)在,在程序的維護(hù)階段,能夠清楚地表達(dá)程序的最初意愿。而實(shí)際上后來(lái)的程序員卻經(jīng)常要猜測(cè)先前的程序員是不是犯了什么錯(cuò)誤,選擇一個(gè)相同的名字,還是他本來(lái)就想重載這個(gè)函數(shù)。
             
             在 Java中,沒(méi)有virtual這個(gè)關(guān)鍵字,所有的方法在底層都是多態(tài)的。當(dāng)方法被定義為static, private或是final時(shí),Java直接調(diào)用它們而不是通過(guò)動(dòng)態(tài)的查表的方式。這意味著在需要被動(dòng)態(tài)調(diào)用時(shí),它們卻是非多態(tài)性的函數(shù),Java的這種動(dòng)態(tài)特性使得編譯器難以進(jìn)行進(jìn)一步的優(yōu)化。
             
             Eiffel和Object Pascal迎合了這個(gè)選項(xiàng)。在它們中,編寫(xiě)子類(lèi)的程序員必須指定他們所想進(jìn)行的重定義動(dòng)作。我們可以從這種做法中得到巨大的好處:對(duì)于以后將要閱讀這些程序的人及程序的將來(lái)維護(hù)者來(lái)說(shuō),可以很容易地找出來(lái)被重寫(xiě)的函數(shù)。因而選項(xiàng)2最好是在子類(lèi)中被實(shí)現(xiàn)。
             
             Eiffel和Object Pascal都優(yōu)化了函數(shù)調(diào)用的方式:因?yàn)樗麄冎恍枰a(chǎn)生那些真正多態(tài)的函數(shù)的調(diào)用分配表的入口項(xiàng)。對(duì)于怎樣做,我們將會(huì)在global analysis這節(jié)中討論。
             
            選項(xiàng)3:

              純虛函數(shù)這樣的做法迎合了讓一個(gè)函數(shù)成為抽象的,從而子類(lèi)在實(shí)例化時(shí)必須為其提供一個(gè)實(shí)現(xiàn)這樣的一個(gè)條件。沒(méi)有重寫(xiě)這些函數(shù)的任何子類(lèi)同樣也是抽象類(lèi)。這個(gè)概念沒(méi)有錯(cuò),但是請(qǐng)你看一看pure virtual functions這一節(jié),我們將在那節(jié)中對(duì)于這種術(shù)語(yǔ)及語(yǔ)法進(jìn)行批判討論。
             
             Java也擁有純虛方法(同樣Eiffel也有),實(shí)現(xiàn)方法是為該方法加上deffered標(biāo)注。
             
            結(jié)論:

             virtual 的主要問(wèn)題在于,它強(qiáng)迫編寫(xiě)父類(lèi)的程序員必須要猜測(cè)函數(shù)在子類(lèi)中是否有多態(tài)性。如果這個(gè)需求沒(méi)有被預(yù)見(jiàn)到,或是為了優(yōu)化、避免動(dòng)態(tài)調(diào)用而沒(méi)有被包含進(jìn)去的話,那么導(dǎo)致的可能性就是極大的封閉,勝過(guò)了開(kāi)放。在C++的實(shí)現(xiàn)中,virtual提高了重寫(xiě)的耦合性,導(dǎo)致了一種容易產(chǎn)生錯(cuò)誤的聯(lián)合。

            Virtual是一種難以掌握的語(yǔ)法,相關(guān)的諸如多態(tài)、動(dòng)態(tài)綁定、重定義以及重寫(xiě)等概念由于面向于問(wèn)題域本身,掌握起來(lái)就相對(duì)容易多了。虛擬函數(shù)的這種實(shí)現(xiàn)機(jī)制要求編譯器為其在class中建立起virtual table入口,而global analysis并不是由編譯器完成的,所以一切的重?fù)?dān)都?jí)涸诹顺绦騿T的肩上了。多態(tài)是目的,虛擬機(jī)制就是手段。Smalltalk, Objective-C, Java和Eiffel都是使用其他的一種不同的方法來(lái)實(shí)現(xiàn)多態(tài)的。
             
             Virtual是一個(gè)例子,展示了C ++在OOP的概念上的混沌不清。程序員必須了解一些底層的概念,甚至要超過(guò)了解那些高層次的面向?qū)ο蟮母拍睢irtual把優(yōu)化留給了程序員;其他的方法則是由編譯器來(lái)優(yōu)化函數(shù)的動(dòng)態(tài)調(diào)用,這樣做可以將那些不需要被動(dòng)態(tài)調(diào)用的分配(即不需要在動(dòng)態(tài)調(diào)用表中存在入口)100%地消除掉。對(duì)于底層機(jī)制,感興趣的應(yīng)該是那些理論家及編譯器實(shí)現(xiàn)者,一般的從業(yè)者則沒(méi)有必要去理解它們,或是通過(guò)使用它們來(lái)搞清楚高層的概念。在實(shí)踐中不得不使用它們是一件單調(diào)乏味的事情,并且還容易導(dǎo)致出錯(cuò),這阻止了軟件在底層技術(shù)及運(yùn)行機(jī)制下(參見(jiàn)并發(fā)程序)的更好適應(yīng),降低了軟件的彈性及可重用性。

            posted on 2007-09-27 02:51 lovedday 閱讀(712) 評(píng)論(4)  編輯 收藏 引用 所屬分類(lèi): ▲ C++ Program

            評(píng)論

            # re: 【ZT】C++批判(1) 2007-09-27 10:53 清源游民

            雖說(shuō)是批判的文章,但從反面給C++學(xué)習(xí)者指導(dǎo)。兼聽(tīng)則明,往往批判會(huì)比贊美對(duì)我們幫助更甚。從理論層面來(lái)討論,對(duì)我這樣知識(shí)比較單薄的人來(lái)說(shuō)有點(diǎn)晦澀,幸好有例子,讓我可以更好理解作者意圖。趕緊發(fā)下一篇吧。  回復(fù)  更多評(píng)論   

            # re: 【ZT】C++批判(1) 2007-09-28 21:20 lovedday

            我覺(jué)得作者說(shuō)的還是挺有道理的,不過(guò)我還得用C++,因?yàn)闆](méi)有更好的選擇,繼續(xù)回去使用C是不太可能的。  回復(fù)  更多評(píng)論   

            # re: 【ZT】C++批判(1) 2007-09-30 08:34 Minidx全文檢索

            選擇合適的語(yǔ)言就可以了吧,。
              回復(fù)  更多評(píng)論   

            # re: 【ZT】C++批判(1) 2008-12-02 14:00 momor

            文章不錯(cuò),用批駁的方式對(duì)問(wèn)題發(fā)起思考,也是一種不錯(cuò)的方式。
            但是對(duì)于某些論調(diào),個(gè)人感覺(jué)不盡然,
            我認(rèn)為:不要夢(mèng)想虛函數(shù)作為你的救世主,它只是語(yǔ)言級(jí)別上對(duì)代碼版本的一種控制管理。我們知道,應(yīng)該盡可能的避免在接口類(lèi)寫(xiě)實(shí)現(xiàn)代碼,同樣你也不應(yīng)當(dāng)將可變功能需求,妄想用虛函數(shù)一次性的解決,解決的方法很多,利用RTTI或者你自己實(shí)現(xiàn)的反射技術(shù)(不要被反射技術(shù)這個(gè)名詞嚇倒,其實(shí)自己在C++中實(shí)現(xiàn)一個(gè)很簡(jiǎn)單),以組合類(lèi)的方式功能添加到你的功能接口類(lèi)中。同樣這也避免了過(guò)渡依賴(lài)?yán)^承導(dǎo)致類(lèi)的過(guò)渡縱向生長(zhǎng),使用組合多于繼承的原則  回復(fù)  更多評(píng)論   

            公告

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            隨筆分類(lèi)(178)

            3D游戲編程相關(guān)鏈接

            搜索

            最新評(píng)論

            久久国产精品国产自线拍免费 | 久久久久久免费一区二区三区 | 久久久久人妻精品一区三寸蜜桃 | 大蕉久久伊人中文字幕| 久久精品人人做人人妻人人玩| 国产一级做a爰片久久毛片| 精品人妻久久久久久888| 久久久久这里只有精品 | 77777亚洲午夜久久多喷| 日本精品久久久久中文字幕| 精品久久久久久国产牛牛app | 久久久久四虎国产精品| 99热成人精品免费久久| 久久99国产综合精品免费| 97久久国产露脸精品国产| 国产精品久久久久9999高清| 久久99精品久久久久久久不卡| 久久精品国产2020| 久久久综合香蕉尹人综合网| 精品久久久噜噜噜久久久| 久久久av波多野一区二区| 亚洲国产成人精品久久久国产成人一区二区三区综 | 国产欧美久久久精品影院| 思思久久精品在热线热| 国产精品成人99久久久久 | 狠狠色婷婷综合天天久久丁香| 中文字幕久久精品无码| 国产美女亚洲精品久久久综合| 久久人人爽人人爽人人片AV东京热| 99久久精品免费看国产一区二区三区| 国产成人无码精品久久久免费 | 久久亚洲高清观看| 久久国产高清字幕中文| 精品99久久aaa一级毛片| 久久er国产精品免费观看2| 国产精品99久久精品| 亚洲精品国产字幕久久不卡| 久久综合九色综合97_久久久| 日韩欧美亚洲国产精品字幕久久久| 91精品久久久久久无码| 热RE99久久精品国产66热|