• <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>

            longshanks

              C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              14 Posts :: 0 Stories :: 214 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(10)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            被誤解的C++——漢尼拔

            by 莫華楓

                公元前216年8月2日,意大利東部平原,一個叫做坎尼的地方,兩支大軍擺開陣勢,準(zhǔn)備決一死戰(zhàn)。一方是由保羅斯和瓦羅兩位執(zhí)政官率領(lǐng)的羅馬人,另一方則是偉大的軍事天才漢尼拔*巴卡率領(lǐng)的迦太基軍隊及其同盟。羅馬人超過8萬,而迦太基僅有4萬余人。然而到了傍晚,羅馬人被徹底擊敗,7萬人被殺,僅有少數(shù)得以逃脫。這就是著名的坎尼會戰(zhàn)。經(jīng)此一役,(外加先前進行的特利比亞和特拉西梅諾湖會戰(zhàn)),羅馬人元氣大傷,成年公民損失達五分之一。部分城邦背叛羅馬,西西里也發(fā)生起義。羅馬已經(jīng)到了搖搖欲墜的地步。

                漢尼拔的這些勝利,完全得益于先前的一次異乎尋常的遠征。公元前218年,漢尼拔率領(lǐng)軍隊,從新迦太基城(西班牙)出發(fā),翻越比利牛斯山,進入南高盧地域。在他面前有兩條路可走,翻越阿爾俾斯山,或者沿海岸進入意大利。但是,當(dāng)時羅馬人已在沿海地區(qū)部署了兩支部隊,準(zhǔn)備攔截漢尼拔。而且,羅馬人的海軍優(yōu)勢,使得他們可以在任何時候?qū)⒁恢Р筷牭顷懺谒谋澈蟆6桨栙滤股剑瑒t是一條及其艱險的道路,更何況是在冬天。

                漢尼拔選擇了阿爾俾斯山。他甩開了羅馬人,從小圣貝納德和日內(nèi)瓦山之間越過阿爾俾斯山,進入意大利境內(nèi)。此時,羅馬人便失去了戰(zhàn)略縱深,一把尖刀已經(jīng)深深地插入他們的腹內(nèi)...

             

                C++的發(fā)展史上,也有著如同漢尼拔翻越阿爾俾斯山遠征。一切還得從C with Class時代說起。

                Bjarne曾經(jīng)反復(fù)強調(diào),他創(chuàng)建C++為的是將Simular的抽象能力同C的性能結(jié)合起來。于是,在C語言的基礎(chǔ)上,誕生了一種擁有類、繼承、重載等等面向?qū)ο髾C制的語言。在這個階段,C++提供了兩個方面的抽象能力。一種是數(shù)據(jù)抽象,也就是將數(shù)據(jù)所要表達的含義通過類型以及依附于類型上的成員表述。另一種則是多態(tài),一種最原始的多態(tài)(重載)。

                數(shù)據(jù)抽象,通過被稱為“抽象數(shù)據(jù)類型(ADT)”的技術(shù)實現(xiàn)。ADT的一種方案,就是類。類所提供的封裝性將一個數(shù)據(jù)實體的外在特征,或者說語義的表述形式,同具體的實現(xiàn),比如數(shù)據(jù)存儲形式,分離。這樣所增加的中間層將數(shù)據(jù)的使用者同數(shù)據(jù)的實現(xiàn)者隔離,使得他們使用共同的約定語義工作,不再相互了解彼此的細節(jié),從而使得兩者得以解耦。

                多態(tài)則是更加基礎(chǔ)更加重要的一種特性。多態(tài)使得我們得以用同一種符號實現(xiàn)某種確定的語義。多態(tài)的精髓在于:以一種形式表達一種語義。在此之前,我們往往被迫使用不同的符號來代表同一種抽象語義,為的是適應(yīng)強類型系統(tǒng)所施加的約束。比如:

            //代碼#1
            int add_int(int lhs, int rhs);
            float add_float(float lhs, float rhs);

                很顯然,這兩個函數(shù)表達的語義分別是“把兩個int類型值加在一起”和“把兩個float類型值加在一起”。這兩個語義抽象起來都表達了一個意思:加。

                我們在做算術(shù)題的時候是不會管被計算的數(shù)字是整數(shù)還是實數(shù)。同樣,如果能夠在編程的時候,不考慮算術(shù)操作對象的類型,只需關(guān)心誰和誰進行什么操作,那么會方便得多。當(dāng)C++引入重載后,這種愿望便得以實現(xiàn):

            //代碼#2
            int add(int lhs, int rhs);
            float add(float lhs, float rhs);

                重載使得我們只需關(guān)心“加”這個語義,至于什么類型和什么類型相加,則由編譯器根據(jù)操作數(shù)的類型自動解析。

                從某種意義上說,重載是被長期忽視,但卻極為重要的一個語言特性。在多數(shù)介紹OOP的書籍中,重載往往被作為OOP的附屬品,放在一些不起眼的地方。它的多態(tài)本質(zhì)也被動多態(tài)的人造光環(huán)所設(shè)遮蔽。然而,重載的重要作用卻在實踐中潛移默化地體現(xiàn)出來。重載差不多可以看作語言邁入現(xiàn)代抽象體系的第一步。它的實際效用甚至要超過被廣為關(guān)注的OOP,而不會像OOP那樣在獲得抽象的同時,伴隨著不小的副作用。

                 隨著虛函數(shù)的引入,C++開始具備了頗具爭議的動多態(tài)技術(shù)。虛函數(shù)是一種依附于類(OOP的類型基礎(chǔ))的多態(tài)技術(shù)。其技術(shù)基礎(chǔ)是后期綁定(late-binding)。當(dāng)一個類D繼承自類B時,它有兩種方法覆蓋(override)B上的某個函數(shù):

            //代碼#3
            class B
            {
            public:
                void fun1();
                virtual void fun2();
            };

            class D:public B
            {
            public:
                void fun1();
                void fun2();
            };

                當(dāng)繼承類D中聲明了同基類B中成員函數(shù)相同函數(shù)名、相同簽名的成員函數(shù),那么基類的成員函數(shù)將被覆蓋。對于基類的非虛成員函數(shù),繼承類會直接將其遮蔽。對于類型D的使用者,fun1代表了D中所賦予的語義。而類型D的實例,可以隱式地轉(zhuǎn)換成類型B的引用b,此時調(diào)用b的fun1,則執(zhí)行的是類B的fun1 定義,而非類D的fun1,盡管此時b實際指向一個D的實例。

                但是,如果繼承類覆蓋了基類的虛函數(shù),那么將得到相反的結(jié)果:當(dāng)調(diào)用引用b的fun2,實際上卻是調(diào)用了D的fun2定義。這表明,覆蓋一個虛函數(shù),將會在繼承類和基類之間的所有層次上執(zhí)行覆蓋。這種徹底的、全方位的覆蓋行為,使得我們可以在繼承類上修飾或擴展基類的功能或行為。這便是OOP擴展機制的基礎(chǔ)。而這種技術(shù)被稱為動多態(tài),意思是基類引用所表達的語義并非取決于基類本身,而是來源于它所指向的實際對象,因此它是“多態(tài)”的。因為一個引用所指向的對象可以在運行時變換,所以它是“動”的。

                隨著動多態(tài)而來的一個“副產(chǎn)品”,卻事實上成為了OOP的核心和支柱。虛函數(shù)的“動多態(tài)”特性將我們引向一個極端的情況:一個都是虛函數(shù)的類。更重要的,這個類上的虛函數(shù)都沒有實現(xiàn),每個虛函數(shù)都未曾指向一個實實在在的函數(shù)體。當(dāng)然,這樣的類是無法直接使用的。有趣的是,這種被稱為“抽象基類”的類,迫使我們繼承它,并“替它”實現(xiàn)那些沒有實現(xiàn)的虛函數(shù)。這樣,對于一個抽象基類的引用,多態(tài)地?fù)碛辛死^承類的行為。而反過來,抽象基類實際上起到了強迫繼承類實現(xiàn)某些特定功能的作用。因此,抽象基類扮演了接口的角色。接口具有兩重作用:一、約束繼承類(實現(xiàn)者)迫使其實現(xiàn)預(yù)定的成員函數(shù)(功能和行為);二、描述了繼承類必定擁有的成員函數(shù)(功能和行為)。這兩種作用促使接口成為了OOP設(shè)計體系的支柱。

                C++在這方面的進步,使其成為一個真正意義上具備現(xiàn)代抽象能力的語言。然而,這種進步并非“翻越阿爾俾斯山”。充其量也只能算作“翻越比利牛斯山”。對于C++而言,真正艱苦的遠征才剛開始,那令人生畏的“阿爾俾斯山”仍在遙遠的前方。

                同漢尼拔一樣,當(dāng)C++一腳邁入“現(xiàn)代抽象語言俱樂部”后,便面臨兩種選擇。或者在原有基礎(chǔ)上修修補補,成為一種OOP語言;或者繼續(xù)前進,翻越那座險峻的山峰。C++的漢尼拔——Bjarne Stroustrup——選擇了后者。

                從D&E的描述中我們可以看到,在C++的原始設(shè)計中就已經(jīng)考慮“類型參數(shù)”的問題。但直到90年代初,才真正意義上地實現(xiàn)了模板。然而,模板只是第一步。諸如Ada等語言中都有類似的機制(泛型,generic),但并未對當(dāng)時的編程技術(shù)產(chǎn)生根本性的影響。

                關(guān)鍵性的成果來源于Alex Stepanov的貢獻。Stepanov在后來被稱為stl的算法-容器庫上所做的努力,使得一種新興的編程技術(shù)——泛型編程(Generic Programming,GP)——進入了人們的視野。stl的產(chǎn)生對C++的模板機制產(chǎn)生了極其重要的影響,促使了模板特化的誕生。模板特化表面上是模板的輔助特性,但是實際上它卻是比“類型參數(shù)”更加本質(zhì)的機能。

                假設(shè)我們有一組函數(shù)執(zhí)行比較兩個對象大小的操作:

            //代碼#4
            int compare(int lhs, int rhs);
            int compare(float lhs, float rhs);
            int compare(string lhs, string rhs);

                重載使得我們可以僅用compare一個函數(shù)名執(zhí)行不同類型的比較操作。但是這些函數(shù)具有一樣的實現(xiàn)代碼。模板的引入,使得我們可以消除這種重復(fù)代碼:

            //代碼#5
            template<typename T> int compare(T lhs, T rhs) {
                if(lhs==rhs)
                    return 0;
                if(lhs>rhs)
                    return 1;
                if(lhs<rhs)
                    return -1;
            }

                這樣一個模板可以應(yīng)用于任何類型,不但用一個符號表達了一個語義,而且用一個實現(xiàn)代替了諸多重復(fù)代碼。這便是GP的基本作用。

                接下來的變化,可以算作真正意義上的“登山”了。

                如果有兩個指針,分別指向兩個相同類型的對象。此時如果我們采用上述compare函數(shù)模板,那么將無法得到所需的結(jié)果。因為此時比較的是兩個指針的值,而不是所指向的對象本身。為了應(yīng)付這種特殊情況,我們需要對compare做“特別處理”:

            //代碼#6
            template<typename T> int compare(T* lhs, T* rhs) {
                if(*lhs==*rhs)
                    return 0;
                if(*lhs>*rhs)
                    return 1;
                if(*lhs<*rhs)
                    return -1;
            }

                這個“特殊版本”的compare,對于任何類型的指針作出響應(yīng)。如果調(diào)用時的實參是一個指針,那么這個“指針版”的compare將會得到優(yōu)先匹配。如果我們將compare改成下面的實現(xiàn),那么就會出現(xiàn)非常有趣的行為:

            //代碼#7
            template<typename T>
            struct comp_impl
            {
                int operator()(T lhs, T rhs) {
                    if(lhs==rhs)
                        return 0;
                    if(lhs>rhs)
                        return 1;
                    if(lhs<rhs)
                        return -1;
                }
            };
            template<typename T>
            struct comp_impl<T*>
            {
                int operator()(T* lhs, T* rhs) {
                    comp_impl<T>()(*lhs, *rhs);
                }
            };
            template<typename T> int compare(T* lhs, T* rhs) {
                comp_impl<T>()(*lhs, *rhs);
            }

                當(dāng)我們將指針的指針作為實參,調(diào)用compare時,神奇的事情發(fā)生了:

            //代碼#8
            double **x, **y;
            compare(x, y);

                compare居然成功地剝離了兩個指針,并且正確地比較了兩個對象的值。這個戲法充分利用了類模板的局部特化和特化解析規(guī)則。根據(jù)規(guī)則,越是特化的模板,越是優(yōu)先匹配。T*版的comp_impl比T版的更加“特化”,會得到優(yōu)先匹配。那么當(dāng)一個指針的指針實例化comp_impl,則會匹配T*版的 comp_impl,因為指針的指針,也是指針。T*版通過局部特化機制,剝離掉一級指針,然后用所得的類型實例化comp_impl。指針的指針剝離掉一級指針,那么還是一個指針,又會匹配T*版。T*版又會剝離掉一級指針,剩下的就是真正可以比較的類型——double。此時,double已無法與 T*版本匹配,只能匹配基礎(chǔ)模板,執(zhí)行真正的比較操作。

                這種奇妙的手法是蘊含在模板特化中一些更加本質(zhì)的機制的結(jié)果。這種意外獲得的“模板衍生產(chǎn)品”可以算作一種編譯時計算的能力,后來被一些“好事者”發(fā)展成獨立的“模板元編程”(Template Meta Programming,TMP)。

                盡管TMP新奇而又奧妙,但終究只是一種輔助技術(shù),用來彌補C++的一些缺陷、做一些擴展,“撿個漏”什么的。不過它為我們帶來了兩點重要的啟示:一、我們有可能通過語言本身的一些機制,進行元編程;二、元編程在一定程度上可以同通用語言一起使用。這些啟示對編程語言的發(fā)展有很好的指導(dǎo)意義。

                模板及特化規(guī)則是C++ GP的核心所在。這些語言特性的強大能力并非憑空而來。實際上有一只“幕后大手”在冥冥之中操縱著一切。

                假設(shè)有一個類型系統(tǒng),包含n個類型:t1,...,tn,那么這些類型構(gòu)成了一個集合T={t1,...,tn}。在當(dāng)我們運用重載技術(shù)時,實際上構(gòu)造了一組類型的tuple到函數(shù)實現(xiàn)的映射:<ti1,ti2,ti3,...> ->fj()。編譯器在重載解析的時候,就是按照這組映射尋找匹配的函數(shù)版本。當(dāng)我們編寫了形如代碼#5的模板,那么就相當(dāng)于構(gòu)建了映射:<T, T,...> ->f0()。

                而代碼#6,以及代碼#7中的T*版模板,實際上是構(gòu)造了一個<Tp>->fp()的映射。這里Tp是T的一個子集:Tp={t'|t'=ti*, ti∈T}。換句話說,特化使泛型體系細化了。利用模板特化技術(shù),我們已經(jīng)能夠(笨拙地)分辨浮點數(shù)、整數(shù)、內(nèi)置類型、內(nèi)置數(shù)組、類、枚舉等等類型。具備為類型劃分的能力,也就是構(gòu)造不同的類型子集的能力。

                現(xiàn)在,我們便可以構(gòu)造一個“泛型體系”:G={T} U T U Tp U Ta U Ti U Tf U Tc ...。其中,Tp是所有指針類型,Ta是數(shù)組,Ti是整數(shù),Tf是浮點數(shù),Tc是類等等。但是如果我們按照泛化程度,把G中的元素排列開:{T, Tp, Ta, Ti,...,t1,...,tn}。我們會發(fā)現(xiàn)這中間存在一些“斷層”。這些斷層位于T和Tp等之間,以及Tp等與ti等之間等等。這表明在C++98/03中,抽象體系不夠完整,存在缺陷。

                所以,到目前為止,C++還沒有真正翻越阿爾俾斯山里那座最險峻的山峰。這正是C++0x正在努力做的,而且勝利在望。

                在C++0x中,大牛們引入了first-class的concept支持。concept目前還沒有正式的法定描述(以及合理的中文翻譯)。通俗地講,concept描述了一個類型的(接口)特征。說具體的,一個concept描述了類型必須具備的公共成員函數(shù),必須具備的施加在該類型上的自由函數(shù)(和操作符),以及必須具備的其他特征(通過間接手段)。下面是一個典型的concept:

            concept has_equal<T>
            {
                bool T::equal(T const& v);
            };

                這個concept告訴我們它所描述的類型必須有一個equal成員,以另一個同類型的對象為參數(shù)。當(dāng)我們將這個concept施加在一個函數(shù)模板上,并作為對類型參數(shù)的約束,那么就表明了這個模板對類型參數(shù)的要求:

            template<has_equal T>bool is_equal(T& lhs, T const& rhs) {
                return lhs.equal(rhs);
            }

                如果實參對象的類型沒有equal成員,那么is_equal將會拒絕編譯通過:這不是我要的!

                concept是可以組合的,正式的術(shù)語叫做“refine”。我們可以通過refine進一步構(gòu)造出約束更強的concept:

            concept my_concept<T> : has_equal<T>, DefaultConstructable<T>, Swappable<T> {}

                refine獲得的concept將會“繼承”那些“基concept”的所有約束。作為更細致的組合手段,concept還可以通過!操作符“去掉”某些內(nèi)涵的concept約束:

            concept my_concept1<T> : has_equal<T>, !DefaultConstructable<T> {}

                這個concept要求類型具備equal成員,但不能有默認(rèn)構(gòu)造函數(shù)。

                通過這些手段,concept可以“無限細分”類型集合T。理論上,我們可以制造一連串只相差一個函數(shù)或者只相差一個參數(shù)的concept。

                一個concept實際上就是構(gòu)成類型集合T的劃分的約束:Tx={ti| Cx(ti)==true, ti∈T}。其中Cx就是concept所構(gòu)造的約束。不同的concept有著不同范圍的約束。這樣,理論上我們可以運用concept枚舉出類型集合 T的所有子集。而這些子集則正好填補了上述G中的那些斷層。換句話說,concept細化了類型劃分的粒度,或者說泛型的粒度。使得“離散”的泛型系統(tǒng)變成“連續(xù)”的。

                當(dāng)我們運用concept約束一個函數(shù)模板的類型參數(shù)時,相當(dāng)于用concept所描述的類型子集構(gòu)建一個映射:<Tx1,Tx2,...>->fx()。凡是符合tuple <Tx1,Tx2,...>的類型組合,對應(yīng)fx()。所以,從這個角度而言,函數(shù)模板的特化(包括concept)可以看作函數(shù)重載的一種擴展。在concept的促進下,我們便可以把函數(shù)模板特化和函數(shù)重載統(tǒng)一在一個體系下處理,使用共同的規(guī)則解析。

                在目前階段,C++差不多已經(jīng)登上了“抽象阿爾俾斯山”的頂峰。但是就如同漢尼拔進入意大利后,還需要面對強盛的羅馬共和國,與之作戰(zhàn)那樣。C++的面前還需要進一步將優(yōu)勢化作勝利。要做的事還很多,其中最重要的,當(dāng)屬構(gòu)建Runtime GP。目前C++的GP是編譯時機制。對于運行時決斷的任務(wù),還需要求助于OOP的動多態(tài)。但是C++領(lǐng)域的大牛們已經(jīng)著手在Runtime GP和Runtime Concept等方面展開努力。這方面的最新成果可以看這里這里這里。相信經(jīng)過若干年的努力后,GP將會完全的成熟,成為真正主流的編程技術(shù)。

             

                坎尼會戰(zhàn)之后,漢尼拔已經(jīng)擁有了絕對的優(yōu)勢。羅馬人已經(jīng)戰(zhàn)敗,他同羅馬城之間已經(jīng)沒有任何強大的敵對力量,羅馬人也已經(jīng)聞風(fēng)喪膽,幾無斗志。但是,漢尼拔卻犯下了或許令他一生后悔的錯誤。他放過了羅馬城,轉(zhuǎn)而攻擊羅馬的南部城邦和同盟。他低估了羅馬人的意志,以及羅馬同盟的牢固程度。羅馬人很快結(jié)束了最初的混亂,任命了新的執(zhí)政官,采用了堅壁清野、以柔克剛的新戰(zhàn)略。隨著時間的推移,漢尼拔和他的軍隊限于孤立無援的境地,被迫為了生存而作戰(zhàn)。盡管迫降并占領(lǐng)了幾個羅馬城市,但是終究無法再次獲得給予羅馬人致命一擊的機會。

                漢尼拔的戰(zhàn)略錯誤實際上在從新迦太基城出發(fā)之前已經(jīng)注定。因為漢尼拔對羅馬人的遠征的根本目的并非擊潰并占領(lǐng)羅馬,而是通過打擊羅馬,削弱他們的勢力,瓦解他們的聯(lián)盟。以達到尋求簽訂和平協(xié)議的目的。然而這種有限戰(zhàn)略卻使導(dǎo)致了他的最終失敗。

             

                不幸的是,C++或多或少地有著同漢尼拔一樣的戰(zhàn)略錯誤。C++最初的目的基本上僅僅局限于“更好的C”。并且全面兼容C。這在當(dāng)時似乎很合理,因為C可以算作最成功的“底層高級語言”,擁有很高的性能和靈活性。但是,C的設(shè)計并未考慮將來會有一個叫做“C++”的語言來對其進行擴展。結(jié)果很多優(yōu)點對于C而言是優(yōu)點,但卻成了C++的負(fù)擔(dān)。比如,C大量使用操作符表達語法結(jié)構(gòu),對于C而言顯得非常簡潔,但對于C++,則使其被迫大規(guī)模復(fù)用操作符,為其后出現(xiàn)的很多語法缺陷埋下了伏筆。這一點上,Ada做得相對成熟些。它從Pascal那里繼承了主要語法,但不考慮兼容。這使得Ada更加完整,易于發(fā)展。新語言就是新語言,過分的兼容是鐐銬,不是優(yōu)勢。而且,合理地繼承語法,同樣可以吸引眾多開發(fā)者。從經(jīng)驗來看,程序員對于語法變化的承受能力還是很強的。他們更多地關(guān)心語言的功能和易用性。

                另一方面,C++最初并未把目標(biāo)定在“創(chuàng)建一種高度抽象,又確保性能的語言”。縱觀C++的發(fā)展,各種抽象機制并非在完整的規(guī)劃或路線圖的指導(dǎo)下加入語言。所有高級特性都是以“添油戰(zhàn)術(shù)”零打碎敲地加入語言。從某種程度上來看,C++更像是一種實驗性語言,而非工業(yè)語言。C++的強大功能和優(yōu)點是長期積累獲得的,而它的諸多缺陷也是長期添加特性的結(jié)果。

                漢尼拔和C++給予我們一個很好的教訓(xùn)。對于一個試圖在1、20年后依然健康成長的語言,那么就必須在最初便擁有明確的目標(biāo)和技術(shù)發(fā)展規(guī)劃。對于以往的語言特性應(yīng)當(dāng)擇優(yōu)而取,不能照單全收。并且在技術(shù)上擁有足夠的前瞻性。我們知道,技術(shù)前瞻性是很難做到的,畢竟技術(shù)發(fā)展太快。如果做不到,那就得有足夠的魄力對過去的東西加以取舍。所謂“舍小就大,棄子爭先”。

                總體而言,C++在抽象機制的發(fā)展方面,還算是成功的。盡管伴隨著不少技術(shù)缺陷,但是C++的抽象能力在各種語言中可稱得上出類拔萃。而且C++還在發(fā)展,它未來將會發(fā)展成什么形態(tài),不得而知。但是,無論C++是繼續(xù)修修補補,還是根本性地變革,它的抽象能力都會不折不扣地保留,并且不斷完善和增強。

             

                坎尼會戰(zhàn)之后,漢尼拔又打過幾次小規(guī)模的勝仗。但經(jīng)過長期的作戰(zhàn),也得不到迦太基的支援,漢尼拔的力量越來越弱,只能在意大利半島上勉強生存。羅馬很快恢復(fù)了元氣,改革了軍事體系和作戰(zhàn)方式,重新掌握了戰(zhàn)略主動權(quán)。更重要的是,羅馬也有了自己的“漢尼拔”——(征服非洲的)普布利烏斯·科爾內(nèi)利烏斯·西庇阿(大西庇阿)。西庇阿被派往北非大陸,直接攻擊迦太基人的老巢。漢尼拔被召回,在扎馬與西庇阿擺開陣勢,展開一場決戰(zhàn)。最終,西庇阿運用從漢尼拔那里學(xué)到的戰(zhàn)術(shù)擊潰了迦太基人,為羅馬人贏得了第二次布匿戰(zhàn)爭的勝利。

                此后,漢尼拔在羅馬人的通緝之下,流亡于地中海沿岸,試圖尋求東山再起的機會。但最終未能如愿,被迫于公元前183年自盡,享年64歲。有趣的是,他的老對手,小他12歲的西庇阿也于同年去世。一個偉大的傳奇就此結(jié)束。

            posted on 2007-12-17 11:28 longshanks 閱讀(2303) 評論(11)  編輯 收藏 引用

            Feedback

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2007-12-18 21:28 范飛龍
            好文
            C++會不會把抽象進行到底呢
            一旦抽象,就真正實現(xiàn)了從“離散”“到”連續(xù)“的跨越了
            OO..  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2007-12-18 22:58 緊握刀鋒
            不錯!  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2007-12-19 13:31 崔友志
            博主真有心 能把C++同歷史聯(lián)系在一起  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2007-12-20 00:51 ni
            適合去搞文學(xué)或者當(dāng)記者什么的.搞技術(shù)或者歷史不合適,缺乏嚴(yán)謹(jǐn)態(tài)度.  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2007-12-20 14:14 秦歌
            挺能聯(lián)想的  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2008-05-20 00:15 bneliao
            寫得不錯。。對c++有了更多的理解  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2009-07-28 11:08 weidagang2046
            >>從某種意義上說,重載是被長期忽視,但卻極為重要的一個語言特性。...它的多態(tài)本質(zhì)也被動多態(tài)的人造光環(huán)所設(shè)遮蔽。

            樓主講的重載多態(tài),我認(rèn)為似乎不夠嚴(yán)格。我理解多態(tài)是同一接口,不同行為。嚴(yán)格來講,重載是不同的接口。泛型則可以認(rèn)為是多態(tài),因為有統(tǒng)一的類型參數(shù)T。  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2009-07-28 14:41 weidagang2046
            我是從其他語言轉(zhuǎn)向c++的,看到c++0x concept的概念,感覺本質(zhì)上就是duck typing。它的作用在于保留了靜態(tài)類型的同時引入非繼承的多態(tài)。

            不過有一點沒有看明白:

            >> template<has_equal T>bool is_equal(T& lhs, T const& rhs) {
            >> return lhs.equal(rhs);
            >> }
            >> 如果實參對象的類型沒有equal成員,那么is_equal將會拒絕編譯通過:這不是我要的!

            這里似乎不需要concept,template<typename T>...即可,編譯器同樣可以做檢查啊。難道這是為了后面提到的runtime concept做的準(zhǔn)備?  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2009-07-28 14:48 ffl
            @weidagang2046
            有了concept之后,多出來的額外信息,好處多多。看劉未鵬的一篇blog里面有介紹。  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2009-07-28 15:20 weidagang2046
            @ffl
            請給個文章鏈接,謝謝!  回復(fù)  更多評論
              

            # re: 被誤解的C++&mdash;&mdash;漢尼拔 2009-07-28 16:20 ffl
            http://blog.csdn.net/pongba/archive/2007/08/04/1726031.aspx  回復(fù)  更多評論
              


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            亚洲国产精品无码久久久秋霞2| 久久综合久久鬼色| 久久久这里只有精品加勒比| 国产精品久久久久久久| 99精品久久久久中文字幕| 国内精品久久久久影院日本| 久久影院综合精品| 国产成人久久精品一区二区三区 | 性欧美大战久久久久久久 | 久久精品国产精品亜洲毛片| 91久久九九无码成人网站| 国产精品免费久久| 久久强奷乱码老熟女网站| 一本大道久久东京热无码AV| 久久久噜噜噜久久中文字幕色伊伊 | 狠狠色丁香婷婷综合久久来来去 | 亚洲精品无码久久久| 亚洲国产综合久久天堂 | 久久91精品国产91久久户| 51久久夜色精品国产| 麻豆久久久9性大片| 久久99精品久久久久子伦| 国产激情久久久久影院| 波多野结衣久久一区二区 | 88久久精品无码一区二区毛片| 精品久久国产一区二区三区香蕉| 三级片免费观看久久| 久久精品国产亚洲av麻豆色欲| 狠狠人妻久久久久久综合蜜桃| 久久综合色老色| av国内精品久久久久影院| 久久免费视频6| 久久99国产综合精品女同| 久久精品综合一区二区三区| 午夜精品久久久久久毛片| 久久国产V一级毛多内射| 亚洲精品无码久久久影院相关影片 | 日韩精品无码久久久久久| 久久综合给合综合久久| 久久精品中文字幕久久| 五月丁香综合激情六月久久|