• <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++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              14 Posts :: 0 Stories :: 214 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(10)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            #

                根據觀察,我發現有兩類程序員。一類程序員喜歡技術,會認認真真地學習一種語言,設法掌握語言的使用要領和方法。他們關心的是語言的功能,以及功能的運用。對于語言的缺陷有相當的容忍度,并且也樂意接受語言的缺陷,只要語言能夠提供足夠強大的功能。
                另一類程序員則相反,他們更側重于用語言實現某些具體的業務。對于他們而言,語言的功能強大與否沒什么關系,只要別妨礙他們在軟件中實現業務。
            對于前者,語言的功能至關重要。他們需要一種語言幫助他們最大限度地發揮智慧和創造力,更快、更好、更高效地構造穩定、可靠、快速、可擴展、可復用的軟件
            而對于后者,語言的簡單至關重要。他們需要一種語言幫助他們最大限度地發揮智慧和創造力,更快、更好、更高效地將業務轉變成軟件的功能
                如果認同一類程序員,而貶損另一類,那就太狹隘了。這兩種程序員對于軟件開發而言,都有各自重要的地位。更重要的是,這兩類程序員是互補的。前者的能力適合開發可擴展的基礎服務和組件,他們是技術專家。而后者則恰好符合業務實現專家的特征。
                然而,我們傳統的組織形式卻將這兩類程序員壓縮在一個共同的空間中執行開發工作。也就是讓他們使用同一種(或同一層次的)語言和技術開發軟件。
                現在的麻煩是,沒有哪一種語言既簡單、方便,又功能強大。如果選用功能強大的語言,比如C++,那么技術專家滿意了,他們構造出漂亮優雅的軟件。但對業務 專家是個災難。他們發現自己已經不知不覺地陷入了語言復雜性的泥潭,而艱難地試圖抓住業務功能的枝干。而反之,選用使用方便,但功能弱小的語言,對于業專 家是個福音,他們可以專注于業務實現,心滿意足地完成工作。但技術專家卻無法按他們的想法達到諸多技術性和軟件工程性的要求,比如性能、可維護性、擴展性 等等。
               最終,多數企業會選擇一種“中性”的語言,功能基本完備,但不很強大,學習和使用相對簡單,但又不是最簡單的。這樣的折中一般會基本“擺平”這兩類程序 員,但也有很多時候讓兩類程序員都不滿意。大多數情況下,即便兩類程序員都滿意了,卻在客觀上使得兩類程序員都無法發揮最大的工作效率,從而無法使開發效 率最大化、最優化。

               解決這類問題最直接的方法就是讓這兩類程序員使用各自適合的語言,在各自擅長的領域開發軟件。技術專家使用C++之類功能強大,卻不易掌握的語言,而業務 專家則使用簡單易用的語言,比如腳本語言、宏語言,甚至是某種特定用途的專用語言(DSL)。技術專家開發基礎服務平臺和組件,業務專家則運用簡易的語言 使用基礎服務和功能,構建業務系統。這種優化組合往往會產生1+1>2的效果。

                對于語言的選擇,技術專家無外乎C++、Ada之類的“全能”通用語言,新興的D也可能成為更加適合的候選人。業務專家,可以使用腳本語言,如 python、ruby、javascript等等“粘合劑”語言。目前尚有一種新的發展方向,是運用專門的專用領域語言(DSL)。這類語言可以非常貼 近業務領域的邏輯概念,語法不一定完備,但足以完成特定的業務工作。比如某種“記賬”語言,就可以用來構造財務軟件的業務邏輯,直接使用財務術語和概念, 最大可能地消除與業務無關的語言要素,達到最簡化的目的。

                這兩類程序員的差異不一定是先天造成的,但這種差異足以對傳統的軟件開發組織形式提出挑戰。因此,當我們在抱怨一門語言如何如何功能不濟,或者如何如何復雜難用,那么請審視一下開發體系,或許一種語言已經被用在不適合的程序員,以及不該用的地方了。

            posted @ 2007-11-22 09:56 longshanks 閱讀(2995) | 評論 (13)編輯 收藏

            vc2008中,mfc將大幅度地更新。我猜測更新mfc的原因,很可能mfc中的一些代碼阻礙了vc的進一步發展,不得不加以更新。
            我以前曾經提起過,mfc有一個嚴重違背c++標準的地方:
            class   H;
            class   S
            {
            public:
                    x()   {
                            H*   pThis=this-offsetof(m_s,   H);
                            ...
                    }
            };
            class   H
            {
                    S   m_s;
            };
            H里包含S的對象,在S里,為了獲得宿主類H的指針,用自身的this指針減去m_s在H中的偏移量。這就要求一個類中的子對象必須同宿主對象放在一起 (連續分布),并且固定(偏移量永遠不變)。為了在對象布局上給予編譯器充分的自由,標準規定offsetof只能用于pod。mfc僅考慮在   vc上使用,所以為了方便而僅僅面向vc編譯器編碼。這帶來了移植性的問題。不過,編譯器間的移植性還是小事。現在我們就可以看到mfc的這種做法是搬起 石頭砸自己的腳。
            sutter和lippman都不止一次地提到將來vc要能夠不區分托管和本地的內存管理。也就是說托管的類型可以在native堆上分配,而   native的類型可以在托管堆上分配。問題來了,由于托管堆上,子對象和宿主對象的存放不是連續的,子對象可能同宿主對象隔著十萬八千里,和成千上萬的 對象。而且子對象可能會在宿主對象的前面。offset也是不確定的。在這種情況下,使用上面的這種代碼無異于自殺。所以,為了實現托管和本地內存管理的 統一,必須放棄offsetof這類畸形代碼。由此導致了mfc的大幅更新。
            另一方面,vc越來越符合標準,而mfc中一些遺留的其他不符合標準的地方,使得編譯器不得不同時應付兩種情況:標準的和非標的。對編譯器著實是個負擔,消除這些非標的東西,反而能夠使得編譯器更加簡單高效。
            以上這些都是猜測,實際如何,還需具體看2008的mfc庫代碼。不管怎么樣,如果你想要使自己的代碼依賴于非標準的特性的話,請三思而后行。
            posted @ 2007-11-15 15:14 longshanks 閱讀(2624) | 評論 (118)編輯 收藏

                 摘要: 編程 是藝術,這無可否認。不信的去看看高大爺的書就明白了。藝術對于我們這些成天擠壓腦漿的程序員而言,是一味滋補的良藥。所以,在這個系列中,每一篇我打算 以藝術的形式開頭。啊?什么形式?當然是最綜合的藝術形式。好吧好吧,就是歌劇。當然,我沒辦法在一篇技術文章的開頭演出一整部歌劇,所以決定用一段詠嘆 調來作為開始。而且,還會盡量使詠嘆調同文章有那么一點關聯,不管這關聯是不是牽強。 噢,我親愛的...  閱讀全文
            posted @ 2007-11-06 16:14 longshanks 閱讀(2028) | 評論 (10)編輯 收藏

            業務邏輯中,很多邏輯上不同類型的東西,到了編程語言中,時常會退化成一種類型。一個最簡單的例子就是貨幣。通常在我們編程時,采用一種類型,如double(有些系統中有專門的Currency類型,為了簡便起見,這里使用double),來表示貨幣。
            但是,隨之而來的就是幣種的問題。不同的幣種間存在換算,也就是匯率的問題。比如我們有RMB和USD兩種貨幣,分別表示人民幣和美元。盡管都是貨幣(在代碼中有相同的類型),我們卻不能對他們直接賦值:
            double rmb_;
            double usd_=100;
            rmb_=usd_;        //絕對不行,100美元可相當于768元人民幣,盡管人民幣在升值
            必須進行匯率換算:
            rmb_=usd_*e_rate;
            e_rate就是匯率。這個誰都清楚。在邏輯上,100美元和768元人民幣是等價的(假設今天的匯率是7.68),是可以兌換的。但在軟件中,我們不能簡單的賦值了事,必須做換算。
            現在我們希望用代碼直接表現邏輯上的意義,也就是用賦值操作:=,實現貨幣間的換算,該怎么做呢?啊對,沒錯,操作符重載。
            我們可以重載operator=操作符,使其具備匯率換算的功能。(或許有人會提出異議,改變一個操作符已有的語義,是否違背大師們的教誨。但我個人認為,語義應當遵從業務邏輯,既然按照邏輯含義進行重載,不應該引發什么糾紛。否則還需要重載干嗎?)但問題是,重載依賴于不同的類型,double operator=(double)的操作符定義是默認的,已經存在,無法以相同形式重載。再說,即便是可以,復制對象和被賦值對象的類型相同,如何區分兩種類型的轉換呢?
            很明顯,我們需要新的類型。typedef肯定是沒指望的,因為它僅僅為一個類型起別名,并沒有產生新的類型。所以,我們只能求助于類。我們可以以如下方式定義各種不同的貨幣類:
            class RMB
            {
            public:
                double    _val;
            };
            class USD
            {
            public:
                double    _val;
            };

            這樣,便可以針對不同貨幣重載operator=:
            class RMB
            {
            public:
                RMB operator=(const RMB& v) {
                    _val=v._val;
                }
                RMB operator=(const USD& v) {
                    _val=v._val*e_rate;    //貨幣換算
                }
            public:
                double    _val;
            };
            class USD
            {
            public:
                USD operator=(const USD& v) {
                    _val=v._val;
                }
                USD operator=(const RMB & v) {
                    _val=v._val/e_rate;    //貨幣換算
                }
            public:
                double    _val;
            };
            這樣,我們便可以對兩種貨幣賦值了:
            RMB    rmb_;
            USD    usd_;
            rmb_=usd_;        //帶貨幣換算的賦值操作
            根據這個方法,我們一直往下推,可以構造出各種各樣的貨幣,并且定義它們之間的轉換:
            class UKP    //英鎊
            {…}
            class JPD    //日元
            {…}

            不過有個問題,如果有10中貨幣,我們必須定義100個operator=的重載,而且都是些重復代碼。這樣太蠢了。得采用更好的方法才能實現我們的理想。
            注意觀察,每個貨幣類的代碼都符合同一種模式,有很強的規律性。看出來了吧,這種情況非常適合使用C++的超級武器——模板。沒錯,說做就做:
            template
            <int CurrType>
            class Currency
            {
            public:
                double    _val;
            };
            注意看,這里非常規地使用了模板的一個特性:非類型模板參數,就是那個int CurrType。模板參數通常都是一個類型,比如int什么的。但也可以是一個非類型的模板參數,就象這里的CurrType。傳統上,非類型模板參數用于傳遞一個靜態的值,用來構造模板類。但在這里,這個模板參數并沒有被模板使用,也永遠不會被使用。這個模板參數的作用就是“制造類型”:
            typedef    Currency
            <0>    RMB;    //人民幣
            typedef    Currency
            <1>    USD;    //美元
            typedef    Currency
            <2>    UKP;    //英鎊
            typedef    Currency
            <3>    JPD;    //日元

            typedef本身不會產生新的類型,但是這里Currency
            <n>已經是完全不同的類型了。當一個模板被實例化成一個類的時候,只要模板參數的實參有所不同,便是一個不同的類型。我們利用了模板的這個特性,憑空制造出任意多個結構完全相同,但卻是完全獨立的類型。
            好,下一步,便是重載operator=操作符。當然不能再做為每一對貨幣類型重載operator=的蠢事了。用一個成員函數模板就可以解決問題:
            double e_rate[10][10];        //匯率表

            template
            <int CurrType>
            class Currency
            {
            public:
                template
            <int ct2>
                Currency
            <CurrType>& operator=(count Currency<ct2>& v) {
                    _val=v._val * e_rate[ct2][CurrType];    //找出匯率表中相應的匯率,
                                                            // 計算并賦值
                }
            public:
                double    _val;
            };
            操作符operator=的代碼中,賦值對象v的值乘上一個匯率,這個匯率存放在匯率表中,通過模板參數CurrType和ct2檢索(當然匯率表得足夠大)。
            這樣,我們便可以隨意地賦值,而無須關心貨幣轉換的問題了:
            ///初始化匯率表
            e_rate[0][0]=1;
            e_rate[1][0]=7.68;

            //使用貨幣
            USD    usd_;
            UKP    ukp_;
            JPD    jpd_;

            jpd_=usd_=ukp=rmb_;    //成功!一切順心。
            需要說明的是,匯率表并沒有在聲明時就初始化,是考慮到匯率經常變動,不應當作為常量寫死在代碼中。更進一步可以使用一個類封裝成可變大小的匯率表,甚至可以用某個文件或數據庫對其初始化。
            問題當然還有,貨幣是要參與運算的,否則沒有什么用處。所以,我們還得使這些貨幣具備基本的計算能力。貨幣的計算,根據業務邏輯大致應具備以下能力:
            1.    +、-:兩種貨幣的加法和減法,允許不同種貨幣參與計算,必須考慮轉換操作,返回做操作數類型;
            2.    *、/:貨幣乘上或除以一個標量值,這里設定為double。但兩種貨幣不能相乘或相除。
            3.    ==、!=:比較兩種貨幣,允許不同種貨幣參與比較,但必須考慮轉換操作。
            還有其他的操作,暫不做考慮,畢竟這里的目的不是開發一個完整的貨幣系統。為了編碼上的方便,這里同時還定義了四則運算的賦值版本:+=、-=、*=、/=。為了節省篇幅,這里只展示+、*和==的代碼,其他代碼類推:
            template
            <int ty, int tp>
            inline bool operator==(currency
            <ty>& c1, const currency<tp>& c2) {
                return    c1._val==c2._val*curr_rate[tp][ty];
            }

            template
            <int ty, int tp>
            inline currency
            <ty>& operator+=(currency<ty>& c1, const currency<tp>& c2) {
                c1._val+=c2._val*curr_rate[tp][ty];
                return    c1;
            }
            template
            <int ty, int tp>
            inline currency
            <ty> operator+(currency<ty>& c1, const currency<tp>& c2) {
                currency
            <ty> t(c1);
                t+=c2;
                return    t;
            }
            請注意==和+操作符中的的貨幣轉換運算,每次都是將第二操作數貨幣轉換成第一操作數貨幣后再進行運算操作。第一參數和第二參數的類型不同,因此允許不同貨幣進行計算。這可以進一步簡化代碼,完全以邏輯的方式編程。
            template
            <int ty>
            inline currency
            <ty>& operator*=(currency<ty>& c1, const double q) {
                c1._val*=q;
                return    c1;
            }
            template
            <int ty>
            inline currency
            <ty> operator*(currency<ty>& c1, const double q) {
                currency
            <T, ty>    t(c1);
                t*=q;
                return    t;
            }

            template
            <int ty>
            inline currency
            <ty>& operator*=(const double q,currency<ty>& c1) {
                return    operator*=(c1, q);
            }
            template
            <int ty>
            inline currency
            <ty> operator*(const double q,currency<ty>& c1) {
                return    operator*(c1, q);
            }

            *操作符的參數只有一個是貨幣類型,另一個是double類型,表示數量。只有貨幣乘上數量才有意義,不是嗎?*操作符包括兩個版本,一個貨幣在前,數量在后;另一個數量在前,貨幣在后。為的是適應rmb_*1.4和1.4*rmb_兩種不同的寫法,算法是完全一樣的。
            現在,貨幣可以運算了:
            usd_=usd_*3;    //同種貨幣運算
            ukp_=rmb_*2.5;        ///計算後直接賦值給另一種貨幣
            jpd_=ukp_=rmb_+usd_;    ///同上,但有四種貨幣參與運算
            現在,貨幣運算非常方便了,不需要考慮貨幣種類,貨幣的轉換是自動的,無需額外代碼。
            在簡化代碼的同時,也提供了操作上的約束,比如:
            ukp_=rmb_*usd_;    ///編譯錯誤。貨幣乘上另一種貨幣無意義!!!
            這句代碼會引發編譯錯誤,因為我們沒有為兩種貨幣相乘提供*的重載。很明顯,一種貨幣與另一種貨幣相乘是根本沒有意義的。這里通過靜態的重載類型檢查,對施加在貨幣上的運算做出約束。促使違背邏輯的代碼在第一時間被攔截,避免出現運行時錯誤。要知道,兩種貨幣相乘,賦給另一個貨幣的錯誤是非常隱蔽的,只有盤庫或結賬的時候才會發現。
            很好,這里我們利用了C++模板的一些特殊機制,以及操作符模板、操作符重載等技術,開發一個貨幣系統。這個系統可以用最簡潔的語句實現各種貨幣的計算和轉換功能。同時,還利用重載機制的強類型特性,提供了符合業務邏輯的操作約束。
            貨幣運算只是一個簡單的案例,但相關的技術可以進一步推廣到更復雜的領域中。而且業務越復雜,所得到的收益越多。因此,充分理解并運用C++所帶來的泛型編程功能,可以大大簡化軟件的開發、減少代碼的錯誤,降低開發的成本。
            這種技術適合用在一些邏輯上存在差異,但在物理上具備相同特征的實體上。一方面使這些實體在代碼中強類型化,以獲得重載和類型檢測能力。由于代碼中邏輯實體的對應類型強類型化,是我們可以通過重載和靜態類型檢測等技術手段,實現僅使用語言提供的要素,在代碼中直接構造業務模型的能力。但手工對每一個邏輯實體進行強類型化,是費力的和瑣碎的,并且存在著大量的重復勞動。此時,我們可以利用模板的抽象能力,反過來利用邏輯實體在物理上的共同特性,一次性構建抽象的模板,并利用模板實例化的一些特性,很方便地構造新的類型(僅僅一個typedef)。
            這種技術進一步擴展后,可以有更高級的應用。一個經典的范例就是實現編譯期的量綱分析。在Template Meta-programming一書中,對此有詳細的講解。

            作為一個好事者,我希望能夠給我周邊的人講解這種技術。他們對C++很不熟悉,但熟悉C#。于是,我打算把這種技術移植到C#中,以便於講解。說做就做。
            我建了一個C#項目,把代碼拷貝過去,然后著手修改,這樣可以省些事。我立刻便遇到了問題。C#有泛型,相當于模板,但不支持非類型泛型參數,即int CurrType,只允許用一個類型作為泛型參數。這樣我們就不能使用C++中耍的手法了(typedef currency
            <n>)。退而求其次,直接用類實現貨幣類型:
            class RMB
            {
            public double _val;
            }
            class USD
            {
            public double _val;
            }

            這樣太繁瑣了,很多重復。我們可以用一個基類封裝_val,貨幣類從基類上繼承獲得:
            class CurrBase
            {
            public double _val;
            }
            class RMB : CurrBase
            {
            }
            class USD : CurrBase
            {
            }

            貨幣類都是空的,它們的存在只是為了創建一個新的類型。
            現在處理貨幣轉換問題。C#不能重載operator=,所以只能使用一個helper函數泛型asign代替:
            class utility
            {
                 public static void asign
            <T1, T2>(T1 c1, T2 c2)
                     where T1 : CurrBase
                     where T2 : CurrBase
                 {
                     c1._val = c2._val * utility.e_rate[c2.CurID(),c1.CurID()];
                 }
            }
            這個asign函數是個泛型,泛型參數分別代表了兩個操作數,函數中執行了貨幣轉換。為了能夠在匯率表中檢索到相應的匯率,我們必須為基類和貨幣類定義抽象函數:
                public abstract class CurrBase
                {
                    public double _val=0;
                    public abstract int CurID();
                }
                public class RMB : CurrBase
                {
                    public override int CurID()
                    {
                        return 0;
                    }
            }

            基類中聲明了CurID()抽象方法,并在貨幣類中定義。這樣,便可以用統一的方式進行貨幣轉換了:
            asign(rmb_, usd_);
            還行,盡管不那么漂亮,但也還算實用。不過,當我多看了幾眼代碼后,便發現這里根本沒有必要使用泛型。完全可以利用OOP的多態實現同樣的功能:
                 public static void asign(CurrBase c1, CurrBase c2)
                 {
                     c1._val = c2._val * utility.e_rate[c2.CurID(),c1.CurID()];
                 }
            不過也沒關系,使用方式還沒有變,代碼反而更簡單了。使用泛型畢竟不是我們的根本目的,對吧?
            現在輪到運算符了。不過我不知該把泛型運算符定義在哪里。按C#文檔里的要求,運算符必須是類的static成員。但我的泛型運算符是針對許多個貨幣類的,定義在任何一個中,對其他類似乎不太公平。于是,我決定嘗試將其定義在基類里:
                public abstract class CurrBase
            {
                …
                    public static CurrBase operator+
            <T1, T2>(T1 c1, T2 c2)
                        where T1 : CurrBase
                        where T2 : CurrBase
                    {
                        …
                    }
            }
            編譯器立刻還我以顏色:操作符根本不能是泛型!好吧,不能就不能吧,繼續退而求其次,用OOP:
                public abstract class CurrBase
            {
                …
                    public static CurrBase operator+(CurrBase c1, CurrBase c2)
                    {
                        …
                    }
            }
            不過,這次讓步讓得有點離譜。當我寫下這樣的代碼時,編譯器居然不認賬:
            rmb_=rmb_+usd_;
            錯誤信息是:錯誤 CS0266: 無法將類型“st_in_cs.CurrBase”隱式轉換為“st_in_cs.RMB”。存在一個顯式轉換(是否缺少強制轉換?)。
            我非得采用強制類型轉換,才能過關:
            rmb_=(RMB)(rmb_+usd_);
            太夸張了,這樣肯定不行。于是,我被迫在每個貨幣類中定義operator+:
            class RMB : CurrBase
            {

                public RMB operator+(RMB c1, USD c2)
                {
                    …
                }
                public RMB operator+(RMB c1, UKP c2)
                {
                    …
                }

            }
            這可不得了,我必須為每對貨幣類定義一個+操作符,+操作符的總數將會是貨幣類數量的平方!其他的操作符每個都是貨幣類數的平方。我可受不了!
            好在,可愛的OOP為我們提供了一根稻草,使得每個貨幣類的每個操作符只需定義一個:
            class RMB : CurrBase
            {

                public RMB operator+(RMB c1, CurrBase c2)
                {
                    …
                }

            }
            這樣,任何貨幣類都可以作為第二操作數參與運算,而操作符只需定義一個。這樣的工作量,對于一個逆來順受的程序員而言,還是可以接受的。很好,代碼不出錯了:
            rmb_=rmb_+usd_;
            但當我寫下如下代碼時,編譯器又開始抱怨了:
            ukp_ = rmb_ + usd_;
            還是要求顯示轉換,除非我們為UKP定義隱式類型轉換操作符:
            class UKP
            {

                public static implicit operator UKP(RMB v)
                {
                    …
                }

            }
            光有RMB的不行啊,還得有USD的、JPD…。不過這樣的話,我們必須為每一個貨幣類定義所有其它貨幣類的類型轉換操作符。又是一個組合爆炸。到這里,我已經黔驢技窮了。誰讓C#不支持=操作符重載和操作符模板化呢。沒辦法,只能忍著點了。
            不過,如果我們能夠降低點要求,事情還是有轉機的。如果我們不通過操作符,而是采用static成員方法,進行貨幣的運算的話,就可以省去很多代碼了:
                public class utility
                {
                    public static T1 asign
            <T1, T2>(T1 c1, T2 c2)
                        where T1 : CurrBase, new()
                        where T2 : CurrBase
                    {
                        c1._val = c2._val * utility.curr_rate[c2.CurID(),c1.CurID()];
                        return    c1;
                    }
                    public static T1 add
            <T1, T2>(T1 c1, T2 c2)
                        where T1 : CurrBase, new()
                        where T2 : CurrBase
                    {
                        T1 t=new T1();
                        t._val=c1._val + c2._val * 
                            utility.curr_rate[c2.CurID(),c1.CurID()];
                        return t;
                    }
                    …
            }
            這里,我還是使用了泛型,因為這些函數需要返回一個值,只有使用泛型,才能返回一個明確的類型,以避免強制轉換的要求。于是,賦值和計算的代碼就成了:
            asign(jpd_, asign(ukp_, add(rmb_, usd_)));//也就是jpd_=ukp_=rmb_+usd_
            的確是難看了點,但是為了能夠少寫點代碼,這也只能將就了。
            好了,我盡了最大的努力,試圖在C#中實現強類型、可計算的貨幣系統。盡管最終我可以在C#中開發出一組與C++具有相同效果的貨幣類(除了賦值操作以外),但需要我編寫大量的代碼,實現各種計算操作,以及貨幣類之間的類型轉換操作(組合爆炸)。相比C++中總共200來行代碼,的確復雜、臃腫得多。
            我并不希望把這篇文章寫成“C++ vs C#”,(盡管我很高興看到C++比C#強?)。我希望通過對這樣一個代碼優化任務,顯示不同技術運用產生的結果。同時,也可以通過這兩種實現嘗試的對比,了解泛型編程的作用,以及泛型編程對語言提出的要求。
            毋庸置疑,C++采用了純粹的泛型編程,因此可以對問題進行高度抽象。并利用問題所提供的每一點有助于抽象的信息,以最簡的形式對問題建模。而作為以OOP為核心的語言C#,對泛型的支持很弱。更重要的是,C#的泛型對泛型參數的運用嚴重依賴於泛型參數的約束(where)。如果沒有where,C#將泛型參數作為Object類型處理,此時泛型參數沒有意義(我無法訪問該類型的成員)。如果有了where,C#要求泛型參數必須同where中指定的類型有繼承關系(如asign中的T1必須是CurrBase的繼承類)。而泛型函數中對泛型參數的使用也局限在約束類型(即CurrBase)上。于是,我們可以直接用以基類(CurrBase)為參數的asign函數代替泛型版本的asign。由于C#對泛型參數的繼承性要求,使得泛型被困住了手腳,無法發揮應用的作用。正由于這些問題,C++才采用了現在模板的形式,而沒有采用同C#一樣的泛型模式。
            或許有人會說,既然OOP能解決問題(asign最初不需要泛型也行,但最終還需要泛型來控制返回值的類型),為什么還要GP呢?
            對于這個問題,前面也給出了答案。由于C#的泛型不支持非類型泛型參數,因此迫使我們使用傳統OOP的手段:利用基類實現貨幣類的實現,定義貨幣類來創建新類型,使貨幣強類型化,利用虛函數提供貨幣獨有信息。僅這一層面,OOP方式已經大大不如GP方式了,GP僅定義了一個模板,所有的貨幣類型都是通過typedef一句語句產生,無需更多的代碼。而OOP方式要求必須為每一個貨幣編寫一個類,代碼量明顯多于GP方式。
            此后,C++通過重載一組操作符模板,實現貨幣的運算。而貨幣模板以及生成貨幣類型的typedef都無須任何改變。而在C#中,由于不支持泛型操作符,被迫定義大量的特定于類型的操作符。所有運算操作符,在每個貨幣類中都必須重載一次。而轉型操作符,則必須在每個貨幣類中重載n-1次。
            換句話說,有n種貨幣,有m個操作符(包括轉型操作符),那么就需要定義n+1個類(包括基類),n×m+n×(n-1)個操作符。假設n=10,m=10,那么總共需要11個類定義,190個操作符重載!如果每個類定義需要20行代碼,而每個操作符重載需要5行代碼,那么總共需要1170行代碼。如果貨幣數量增加,總的代碼數將以幾何級數的方式增長。
            上面的計算表明,盡管OOP可以解決問題,實現我們的目標,但所帶來的開發量和維護量卻是難以承受的。而且,OOP的方式擴展非常困難,隨著系統規模的擴大,擴展將越來越困難。所有這些都表明一點,盡管OOP是軟件工程的明星,但在實際情況下,很多地方存在著OOP無法解決或難以解決的問題。這也就是為什么業界的先鋒人物不斷拓展和強化泛型編程的原因。
            posted @ 2007-05-31 14:25 longshanks 閱讀(1470) | 評論 (6)編輯 收藏

            僅列出標題
            共2頁: 1 2 
            伊人久久无码中文字幕| 日本强好片久久久久久AAA| 日本道色综合久久影院| 久久亚洲国产中v天仙www | 国产午夜久久影院| 精品久久久久久久久久久久久久久 | 丰满少妇人妻久久久久久| 久久精品视频免费| AV无码久久久久不卡蜜桃| 久久不射电影网| 国产精品99久久99久久久| 久久影视国产亚洲| 久久99精品久久久久久9蜜桃 | 亚洲人成网亚洲欧洲无码久久 | 一级a性色生活片久久无| 一本色道久久88加勒比—综合| 亚洲精品无码成人片久久| 一本一本久久a久久精品综合麻豆| 久久国产高清字幕中文| 久久本道伊人久久| 国产精品女同一区二区久久| 中文精品久久久久国产网址| 91精品婷婷国产综合久久| 韩国三级中文字幕hd久久精品| 久久香蕉国产线看观看乱码 | 色综合久久久久久久久五月| 精品国产乱码久久久久软件| 大伊人青草狠狠久久| 久久成人精品| 久久成人国产精品| 久久激情五月丁香伊人| 日韩精品无码久久久久久| 国产高潮国产高潮久久久91 | 亚洲精品99久久久久中文字幕| 一本色道久久88综合日韩精品| 97久久国产露脸精品国产| 精品久久人妻av中文字幕| 久久99精品九九九久久婷婷| 亚洲精品午夜国产VA久久成人 | 久久综合香蕉国产蜜臀AV| 国产精品成人久久久|