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

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            編程 是藝術,這無可否認。不信的去看看高大爺的書就明白了。藝術對于我們這些成天擠壓腦漿的程序員而言,是一味滋補的良藥。所以,在這個系列中,每一篇我打算 以藝術的形式開頭。啊?什么形式?當然是最綜合的藝術形式。好吧好吧,就是歌劇。當然,我沒辦法在一篇技術文章的開頭演出一整部歌劇,所以決定用一段詠嘆 調來作為開始。而且,還會盡量使詠嘆調同文章有那么一點關聯,不管這關聯是不是牽強。

            噢,我親愛的++

            普契尼的獨幕歌劇歌劇《賈尼·斯基基》完成于1918年,同年初演于紐約。

            本劇的劇情取自意大利詩人但丁(1265 1321)的長詩《神曲·地獄篇》中的一個故事:富商多納蒂死了。其遺囑內,將遺產全數捐獻給某一教堂。在場親友大失所望。眾人請賈尼·斯基基假扮多納蒂,騙過公證人,另立遺囑,遺產由眾親友均分。公證人到場。結果斯基基將少量遺產分與眾人,大部分留給了自己。遺囑錄畢,公證人離去。眾大嘩,斯基基從病榻躍起,持棒驅散眾人。

            劇中斯基基的女兒勞蕾塔為表達對青年努奇奧的愛情,對其父唱起了這首美妙絕倫的詠嘆調——“我親愛的爸爸”:

            “啊! 我親愛的爸爸,我愛那美麗少年。
            我愿到露薩港去,買一個結婚戒指。
            我無論如何要去,假如您不答應,
            我就到威克橋上,縱身投入那河水里。
            我多痛苦,我多悲傷。
            ! 天哪! 我寧愿死去!
            爸爸,我懇求你!
            爸爸,我懇求你!

             

            按照C/C++中對于后置操作符++的定義,操作數增加1,并返回原來的值。于是,有人根據這個給C++遍了一段笑話,流傳甚廣。那么,C++是否相對C加了那么一點點,然后還是返回原來的值呢?那就讓我們來“實地考察”一下,了解這個++究竟加了多少。

            我不打算羅列C++的各種紛繁復雜的特性。已經有無數書籍文章做了這件事,肯定比我做的好得多。我要做的,是探索如何運用C++的一些機制,讓我們能夠更方便、快捷、容錯地開發軟件。這些特性很多都是非常簡單的,基本的。正因為它們基本,很容易為人們所忽略。另一些則是高級的,需要多花些時間加以掌握的。但是,這些特性也具有一些簡單,但卻非常實用、靈活和高效的用法。

            相對于CC++最主要的變化就是增加了類。嚴格地講,類是一種“用戶定義類型”,是擴展類型系統的重要手段。類從本質上來說,是一種ADTAbstract Data Type,抽象數據類型)。籠統地講,ADT可以看作數據和作用在這些數據上的操作的集合。

            類提供了一種特性,稱為可見性。意思是說,程序員可以按自己的要求,把類上的數據或函數隱藏起來,不給其他人訪問。于是,通過可見性的控制,可以讓一個類外部呈現一種“外觀”,而內部可以使用任何可能的方法實現類的功能。這稱為“封裝”。

            呵呵,聽煩了吧。這些東西是學過C++(或者任何時髦的OOP語言)的都已經爛熟于胸了。這樣的話,我們就來點實際的,做個小案例,復習復習。溫故而知新嘛。:)

            案例非常簡單,做一個圓類。讓我們從“赤裸”的C結構開始吧:

            struct Cycle
            {
               
            float   center_x;
               
            float   center_y;
               
            float   radius;
            };
                    很傳統的表示,<圓心坐標,半徑>,便可以立刻定義出一個圓形。現在,假設我們需要計算圓形的面積。于是,我寫了一個函數執行這項任務:
            float Area(const Cycle & rc) {
               
            return  PI*rc.radius*rc.radius;
            }
                很好。但是突然有一天,我心血來潮,把圓形類的存儲改成外切正方形的<左上角,右下角>形式,那么這個函數就不能用了。為了讓我這么一個三心二意的人能夠得到滿足,就得運用封裝這個特性了:
            class Cycle
            {
            public:
               
            float get_center_x() { return left; }
               
            float get_center_y() { return top; }
               
            float get_radius() { return bottom; }

            private:
               
            float   center_x;
               
            float   center_y;
               
            float   radius;
            };

            然后,面積計算公式稍作改動就行了:

            float Area(const Cycle & rc) {
               
            return  PI*rc. get_radius()*rc. get_radius();
            }

            這時,如果我改變了Rectangle的數據存儲方式,也不會影響Area函數:

            class Cycle
            {
            public:
               
            float get_center_x() { return (left+right)/2; }
               
            float get_center_y() { return (top+bottom)/2; }
               
            float get_radius() { return (right-left)/2; }

            private:
               
            float   left;
               
            float   top;
               
            float   right;
               
            float   bottom;
            };

            運用了封裝之后,類的實現和接口分離了。于是我們便可以在使用方神不知鬼不覺的情況下,改變我們的實現,以獲得更好的利益,比如效率的提升、代碼維護性的提高等等。

            當我們嘗到封裝的甜頭之后,便會繼續發揚光大:

            class Cycle
            {
            public:
               
            float get_center_x() { return left; }
               
            float get_center_y() { return top; }
               
            float get_radius() { return bottom; }

              
            float get_left() { return center_x-radius; }
              
            float get_right() { return center_x+radius; }
              
            float get_top() { return center_y-radius; }
              
            float get_bottom() { return center_y+radius; }

              
            float area() { return    PI*get_radius()*get_radius(); }

            private:
               …
            };


            作為一個思想純正的OOP程序員而言,這是一個漂亮的設計。不過,對于我這樣一個同樣思想純正的Multiple-paradigm程序員而言,這是個不恰當的設計。

            我承認,這個設計完成了工作,達到了設計目標。但是,這種被Herb Sutter稱為“單片式”的設計是一種典型的過度OO的行為。Sutter在他的《Exceptional C++ Style》一書中,用了最后四個條款,詳細地批判了以std::string為代表的這種設計。

            這里,沒有那么復雜的案例,我就簡單地介紹其中存在的一些問題,其余的,請看Sutter的書。首先,當Cycle的內部存儲形式發生變化時,需要修改不只一個地方:

            class Cycle
            {
            public:
               
            float get_center_x() { return (left+right)/2; }
               
            float get_center_y() { return (top+bottom)/2; }
               
            float get_radius() { return (right-left)/2; }

              
            float get_left() { return left; }
              
            float get_right() { return top; }
              
            float get_top() { return right; }
              
            float get_bottom() { return bottom; }

            private:
               
            float   left;
               
            float   top;
               
            float   right;
               
            float   bottom;
            };

            當然,如果get_left()等成員函數通過get_center_x()等成員函數計算獲得:

            float get_left() return get_center_x()-get_radius(); }

            這樣在改變數據存儲的情況下,修改get_left()等函數了。不過,get_center_x()等函數本來就是從left等成員數據上計算獲得,get_left()再逆向計算回去,顯得有些奇怪。

            這還只是小問題。更重要的是增加了這些冗余的函數,使得類在接口的靈活性上變差。假設我們在Cycle類上增加一個offset()函數,實現平移:

            class Cycle
            {
            public:
               …
              
            void offset(point o) { center_x+=x; center_y+=y; }
               …
            };

            Cycle的使用代碼中,調用了offset()

            Cycle c;

            c.offset(
            20100);

            假設,此時來了一個需求,要求offset()可以接受size類的對象作為參數。那么就必須修改Cycle類的定義,改變或重載offset()。如果這個Cycle是別人寫的,不是我們所能改變的,那么事情就比較麻煩。

            按照現代的Multiple-paradigm的設計理念,這類操作應當以non-member non-friend的形式出現,而類僅僅保持最小的、無冗余的接口集合:

            void offset(Cycle& c, point o) {
               c.set_center_x(c.get_center_x()
            +o.x); //如果有屬性,就更好了:)
               c.set_center_y(c.get_center_y()+o.y);
            }

            此時,如果需求改變,那么只需編寫一個函數重載,便可以解決問題,而無需考慮類的修改了。

            關于這方面的問題,Meyes有一篇很有見地的文章:http://www.ddj.com/cpp/184401197。作者認為,冗余的成員函數實際上只會降低類的封裝性,而不是提高。這看似一個嘩眾取寵的論點,但是Meyes所給出的論據卻非常具有吸引力。他給出了一個“封裝性”的具體度量:封裝性的好壞取決于類實現變化時,對使用代碼產生的影響。類的接口的冗余度越大,越容易受到實現變化的影響。

            所以,現在主流的C++社群都提倡用小類+non-member non-friend函數實現,以提高靈活性。這一點反過來也更接近計算機軟件“數據+操作”的本質。

            經過長時間的開發工作,我們逐步積累起很多圓類,都是面向不同實現。有的通過傳統的<圓心,半徑>存放數據;有的通過外接正方形坐標保存數據;有的通過一個長軸等于短軸的橢圓存放數據;有的通過內接正方形保存數據;。不過它們的接口都是相同的,即<圓心,半徑>形式。

            面對這些圓的實現,為它們各自開發一套算法實在讓人泄氣。大量的重復代碼,和重復勞動,簡直是對程序員的智慧的侮辱。我們需要開發一套算法,然后用于所有圓類。這就需要動用C++MDW(大規模殺傷性武器)——模板:

            template<typename T>
            void offset(T& c, float x, float y) {
               c.set_center_x(c.get_center_x()
            +o.x);
               c.set_center_y(c.get_center_y()
            +o.y);
            }

            這樣,同一個算法便可以用于(我們)所有的圓類:

            CycleA c1;
            CycleB c2;
            CycleC c3;

            offset(c1, 
            1020);
            offset(c2, 
            2700);
            offset(c3, 
            999);

            不過,有些頑固的人認定一個圓應當用外接正方形的形式定義(接口形式是外接正方形的坐標)。并且基于這種構造,開發了一堆有用的函數模板。比如說inflate<>()

            可我們這些理智的人已經開發了<圓心,半徑>形式的Cycle。只是看中了頑固派的哪些操作函數,希望能夠重用一下,免得自己重復勞動。同時,我們又不希望重做一個Cycle類,來符合那些缺乏理智的Cycle定義。

            怎么辦?設計模式告訴我們,可以用Adapter解決問題:

            class CycleAdapter
            {
            public:
               CycleAdapter(Ours::Cycle 
            const& c) : m_cycle(c){}

               
            float get_left() { return Ours::getLeft(m_cycle); }
               
            void set_left(float left) { return Ours::setLeft(m_cycle, left); }
               …
            private:
               Cycle
            & m_cycle;
            };

            此后,我們便可以使用頑固派的函數了:

            Ours::Cycle c;

            CycleAdapter ca(c);
            Theirs::inflate(ca, 
            1.5);

            唉,世事難料,上頭下命令,必須同時使用我們自己的圓類和頑固派的圓類。(肯定是收了他們的好處了)。沒辦法,命令終究是命令。可從今往后,我們就得同時開發兩套算法。痛苦。不過相比使用算法的人來說,我們還算幸運的。他們必須不斷地在OursTheirs命名空間里跳來跳去,時間長了難保不出錯。

            算法使用者希望一個算法就是一個名字,在同一個命名空間,以免混亂。幸運的是,在一種未來技術的支持下,我們做到了。這就是C++BM(彈道導彈,MDW的運載器)——concept

            concept OurCycle<typename T> {
               
            float T::get_left();
               
            void T::set_left(float left);
               …
            }

            concept TheirCycle
            <typename T> {
               
            float T::get_left();
               
            void T::set_left(float left);
               …
            }

            concept_map OurCycle
            <Ours::CycleA>;
            concept_map OurCycle
            <Ours::CycleB>;


            concept_map TheirCycle
            <Theirs::CycleA>;
            concept_map TheirCycle
            <Theirs::CycleB>;


            template
            <OurCycle T> void move(T& c, point const& p) {…}  //#1
            template<TheirCycle T> void move(T& c, point const& p) {…} //#2

            concept和特化的共同作用下,我們便可以很方便地(不需考慮我們的,還是他們的)使用這些算法了:

            Ours::CycleA c1;
            Ours::CycleB c2;
            Theirs::CycleA c3;

            move(c1, point(
            20,3));     //調用#1
            move(c2, point(5111));   //調用#1
            move(c3, point(722));    //調用#2

            隨著應用的發展,我們不僅僅需要操作一個圖形,還要把它畫出來。這件事不算難。但是,面對不同的需求,我們有完全不同的兩套方案。

            先看一下常見的方案——OOP。這是經典的OOP案例,我就簡單地描述一下,諸位別嫌我羅嗦J。為了方便,這里用mfc作為繪圖平臺,盡管我討厭mfc

            定義一個抽象類:

            class Graph
            {
               …
            public:
               
            virtual void Draw(CDC& dc)=0;
            };

            所有圖形類從Graph繼承而來,并且重寫(overrideDraw()

            class Cycle : public Graph
            {
               …
            public:
               
            void Draw(CDC& dc) {
                   … 
            //繪制圓
               }
            };

            class Rectangle : public Graph
            {
               …
            public:
               
            void Draw(CDC& dc) {
                   … 
            //繪制矩形
               }
            };

            此后,便可以創建一個對象并繪制:

            Cycle c;
            c.Draw(dc);

            但這同不用虛函數有什么區別?請看以下代碼:

            typedef shared_ptr<Graph> GraphPtr;
            vector
            <GraphPtr>   gv;
            gv.push_back(GraphPtr(
            new Cycle));
            gv.push_back(GraphPtr(
            new Rectangle));
            gv.push_back(GraphPtr(
            new Line));

            for_each(gv.begin(), gv.end(), mem_fun(
            &Graph::Draw));

            (附注:我這里不辭辛勞地用了智能指針,為的是無憂無慮地編寫代碼,不必為資源的安全而煩惱。同時,標準算法for_each和成員函數適配器mem_fun的使用也是為了獲得更簡潔、更可靠的代碼。這些都是應當廣泛推薦的做法,特別是初學者)。

            拋開智能指針,gv中包含的是基類Graph的指針,當各種繼承自Graph的對象插入gv時,多態地轉換成基類Graph的指針。當后面for_each算法執行時,它會依次取出gv的每一個元素,并通過mem_fun適配器調用每個元素(即Graph指針)上的Draw成員函數。(關于for_eachmem_fun的奇妙原理,我這里就不說了,有很多參考書都有很詳細的解釋,比如《C++ STL》、《C++ Standard Library》等等)。

            這里的核心在于,當我們調用Graph指針上的Draw成員函數時,實際上被轉而定向到繼承類(CycleRectangle等)的Draw()成員函數上。這個功能非常有用,也就是說,當一組類(Cycle等)繼承自同一個基類(Graph)后,可以通過覆蓋基類上的虛函數(Draw)實現對基類行為的修改和擴充。同時,基類(Graph)成為了繼承類(Cycle等)的共同接口,通過接口我們可以將不同類型的對象放在同一個容器中。這種技術可以避免大量switch/case的硬編碼分支代碼,(也稱為tag dispatch),大大簡化我們軟件的構架。同時也可以大幅提高性能,操作分派可以從O(n)復雜度變成O(1)hash_map)或O(logN)map)。

            這種通常被稱為“動多態”的OOP機制,允許我們在運行時,根據某些輸入,比如從一個圖形腳本文件中讀取圖形數據,創建對象,并統一存放在唯一容器中,所有圖形對象都以一致的方式處理,極大地優化了體系結構。

            有“動”必有“靜”。既然有“動多態”,就有“靜多態”。所謂“靜多態”是指模板(或泛型)帶來的一種多態行為。關于模板前面我們已經小有嘗試,現在我們通過模板上的一些特殊機制,來實現一種多態行為。

            作為獨立于OOP的一種新的(其實也不怎么新,其理論根源可以追溯到1967年以前)范式,模板(泛型)相關的編程被稱為“泛型編程”(GP)。gp最常用的一種風格就是算法獨立于類,這在前面我們已經看到過了。所以,這里的Draw也作為自由函數模板:

            template<typename T> void Draw(CDC& dc, T& g);

            template
            <> void Draw<Cycle>(CDC& dc, Cycle& g) {  //#1
               dc. Ellipse(get_left(g), get_top(g), get_right(g), get_bottom(g));
            }

            template
            <> void Draw<Rectangle>(CDC& dc, Rectangle& g) { //#2
               dc. Rectangle(get_left(g), get_top(g), get_right(g), get_bottom(g));
            }

            當用不同的圖形對象調用Draw時,編譯器會自動匹配不同的版本:

            Cycle c;
            Rectangle r;

            Draw(dc, c);   
            //#1
            Draw(dc, r);   //#2

            這里使用了函數模板特化這種特性,促使編譯器在編譯時即根據特化的情況調用合適的函數模板版本。不過,仔細看函數模板的聲明,會發現這同函數的重載幾乎一樣。實際上此時使用函數重載更加恰當。(函數重載通常也被認為是一種多態)。這里使用模板,是為了引出未來的concept的方案:

            template<OurCycle T> void Draw(CDC& dc, T& g) {    //#1
               dc. Ellipse(get_left(g), get_top(g), get_right(g), get_bottom(g));
            }

            template
            <OurRectangle T> void Draw(CDC& dc, T& g) {    //#2
               dc. Rectangle(get_left(g), get_top(g), get_right(g), get_bottom(g));
            }

            同前面的move模板一樣,這里的Draw也實現了編譯期的操作分派(以類型為tag)。此時,我們便可以看出,引入了concept之后,模板的特化(針對concept)不僅僅使得代碼重用率提高,而且其形式同函數重載更加類似。也就是說,重載多態和函數模板的靜多態有了相同的含義(語義),兩者趨向于統一。

            以上代碼另一個值得注意的地方是get_left()等函數。這些函數實際上是函數模板,分別針對不同的類型特化。這使得所有相同語義的操作,都以同樣的形式表現。對于優化開發,提高效率,這種形式具有非常重要的作用。

            模板的這種靜多態同OOP的動多態有著完全不同的應用領域。更重要的是,兩者是互補的。前者是編譯時執行的多態,具有很高的靈活性、擴展性和運行效率;后者是運行時執行的多態,具備隨機應變的響應特性。所以,通常情況下,凡是能在開發時確定的多態形式,比如上述代碼中get_left是可以在編譯時明確調用版本,適合使用模板。反之,只能在運行時確定的多態行為,比如從圖形腳本文件中讀取的圖形數據,則應當使用OOP

            最后,這里還將涉及一種非常簡單,但卻極其實用的C++特性:RAII。所謂RAII,是Bjarne為一種資源管理形式所起的笨拙的名字,全稱是Resource Acquisition Is Initialization。其實這個名稱并不能表達這種技術的特征。簡單地講,就是在構造函數中分配資源,在析構函數中加以釋放。由于C++的自動對象,包括棧對象、一個對象的子對象等等,在對象生成和初始化時調用構造函數,在對象生命期結束時調用析構函數。所以,RAII這種資源管理形式是自動的和隱含的。下面用文件句柄來做一個說明:

            class file
            {
            public:
               file(
            string const& fn) {
                   m_hFile
            =open_file(fn.c_str());//假設open_file是C函數,close也一樣
                   if(0==m_hFile)
                       
            throw exception(“open file failed!”);
               }
               
            ~file() {
                   close(m_hFile);
               }
            private:
               handle  m_hFile;
            };

            在一個函數中,當我們使用這個類時,可以無需考慮如何獲取和釋放資源,同時也保證了異常的安全:

            void fun() {
               file f(“x.txt”);
               …  
            //利用f進行操作,可能會拋出異常
            }  //當函數返回或異常拋出,棧清理的時候,會自動調用file::~file(),釋放文件句柄

            這樣,資源管理會變得非常簡單、方便,即便是最鐵桿的C程序員,也能從中獲得很大的好處。

            而且,RAII不僅僅可以用來管理資源,還可以管理任何類似資源的東西(也就是有借有還的東西)。我們還是拿繪圖作為案例。

            用過mfc的都知道,有時我們需要改變dc的設置,比如pen的寬度、brush的顏色等等,在繪圖完成之后在回到原來的設置。mfc(確切地說是Win32)提供了一對函數,允許我們把原先的dc設置保存下來,在完成繪圖后在恢復:

            void Draw(CDC& dc, Cycle& c) {
               
            int old_dc=dc.SaveDC();
               …  
            //使用dc,可能拋異常
               dc.RestoreDC(old_dc);
            }

            這種“赤裸裸”地使用Save/RestoreDC并非是件好事,程序員可能忘記調用RestoreDC返回原來狀態,或者程序拋出異常,使得dc沒機會Restore。利用RAII,我們便可以很優雅地解決這類問題:

            class StoreDC
            {
            public:
               StoreDC(CDC
            & dc): m_dc(dc) {
                   m_stored
            =m_dc.SaveDC();
                   
            if(0==m_stored)
                       
            throw exception(“DC is not saved.”);
               }
               
            ~StoreDC() {
                   m_dc.RestoreDC(m_stored);
               }
            private:
               CDC
            & m_dc;
               
            int m_stored;
            };

            此后,可以很簡單地處理dcRestore問題:

            void Draw(CDC& dc, Cycle& c) {
               StoreDC    sd(dc);
               …  
            //使用dc,可能拋異常
            }  //函數結束時,會自動RestoreDC,無論正常退出還是拋出異常

            除此以外,RAII還可以用于維持commit or rollback語義等等方面。關于這些內容,可以參考一本非常實用的書:《Imperfect C++》。

             

            C++擁有很多非常好的和實用的機制,限于篇幅(以及我未來的文章J)只能就此打住。這里我蜻蜓點水般的掃描了一下C++的一些主要的特性,意圖告訴大家,如果你覺得C++并沒有加多少,那么還是請認真地了解一下真正的C++。盡管C++在這些特性之外,存在很多弊病,并非那么容易掌握。但是,了解這些基本的特性,對于程序員,無論是否使用C++,都有非常大的幫助。

            最近,一次普通的開發活動,讓我突然意識到其實有很多實際開發中的問題,還是仰賴一些非常基礎和簡單的特性。關鍵在于如何認識和正確使用這些特性。于是,我開始漸漸地將一部分目光從高深的技術和特性轉向如何更好地使用這些基本特性。從這一點上來看,C++社群需要AbramsAlexandrescu這類牛人,但更需要Matthew Wilson這樣的實踐者。大多數情況下,Matthew這樣的實踐牛人對于整個社群更重要。

            對于C++的各種負面誤解可能并不會對C++產生實質性的傷害。而傷害最大的,反而是過度宣揚和不切實際地推廣那些極端機巧的技術和方法。這些技術的作用無可否認,但在尚未掌握C++基本使用技能的人群中推廣,就好比教小學生寫學術論文那樣不切實際。結果很容易造成,要么鉆入技術牛角尖,要么被嚇跑。

                總之一句話,基礎更重要
            posted on 2007-11-06 16:14 longshanks 閱讀(2024) 評論(10)  編輯 收藏 引用

            Feedback

            # re: C++之歌——噢,我親愛的++ 2007-11-06 16:16 <a href=http://minidx.com>minidxer</a>
            呵呵,有意思  回復  更多評論
              

            # re: C++之歌——噢,我親愛的++ 2007-11-06 22:20 every
            Good Work!
            C++ 需要更多這樣的普及文章。  回復  更多評論
              

            # re: C++之歌——噢,我親愛的++ 2007-11-07 08:59 tiro
            不錯!希望老大多寫些這樣清晰易懂又實用性強的文章~·  回復  更多評論
              

            # re: C++之歌——噢,我親愛的++ 2007-11-07 10:42 diego
            good work

            厲害啊



            歡迎來到國內最好的壁紙論壇 5D壁紙 http://5d6p.5d6d.com  回復  更多評論
              

            # re: C++之歌——噢,我親愛的++ 2007-11-07 12:06 空明流轉
            一開始還以為是新手。。。后來發現原來是牛人啊。

            不錯,受教了。

            但是有關non member non friend,我個人認為Sutter有些偏執了。
            如果這一類的函數存在這充分的復用條件,那么選擇這樣的形式是有著正當理由的;否則的話,沒有更多的理由講一個函數從它唯一相關的類中剝離出來。
            因為即使剝離出來,由于函數僅被單個類使用,因此并沒有顯著的降低客戶代碼與類之間的耦合,也同樣沒有讓類在邏輯上更加容易被人理解,只是類里面的代碼少了一些而已。  回復  更多評論
              

            # re: C++之歌——噢,我親愛的++ 2007-11-07 14:05 longshanks
            non member non friend這一點上,Sutter的案例有些極端。但他的思想代表了整個C++社群,或者說現代multiple-pariadm風格的主流。而meyes的文章則更加理論化。
            這種函數的分離帶來兩個好處:首先,就是靈活性。無論如何成員函數的靈活性無法同自由函數相比。我們無法得到一致性的論斷,自由函數絕對好,但是可以明確自由函數更靈活,更易于替換,特別是有能力在維持原有的訪問形式之下,擴種針對一個類的操作,這是成員函數無法做到的。
            另一個方面,自由函數更利于泛化,構造通用的算法或算法框架,提高整個設計的擴展性。
            使用自有函數基本上沒有什么副作用,但成員函數由于被強行綁定在類上,無法自由變換。  回復  更多評論
              

            # re: C++之歌——噢,我親愛的++ 2007-11-08 00:04 上帝也缺錢
            強啊,雖然我看的不是很懂,但我明白我和你的差距實在是大啊!  回復  更多評論
              

            # re: C++之歌——噢,我親愛的++ 2007-11-08 20:51 congcong
            受教。。。。。。。牛!!  回復  更多評論
              

            # re: C++之歌——噢,我親愛的++ 2007-12-19 21:25 ffl
            好  回復  更多評論
              

            # re: C++之歌——噢,我親愛的++ 2008-08-07 11:57 AlexEric
            深入淺出 C++  回復  更多評論
              

            香蕉久久夜色精品国产2020| 久久久国产精品福利免费| 久久综合成人网| 久久精品日日躁夜夜躁欧美| 久久天天躁狠狠躁夜夜网站| 9191精品国产免费久久| 亚洲人成无码久久电影网站| 亚洲女久久久噜噜噜熟女| 色成年激情久久综合| 久久人人爽人人爽人人片AV东京热| 亚洲精品无码久久久久| 久久99精品久久久久久不卡| AV无码久久久久不卡蜜桃| 欧美伊香蕉久久综合类网站| 思思久久99热只有频精品66| 久久国产精品-国产精品| 久久久久久国产精品美女| 91精品日韩人妻无码久久不卡| 精品伊人久久大线蕉色首页| 久久久久亚洲精品无码网址| 麻豆成人久久精品二区三区免费 | 人妻丰满?V无码久久不卡| 思思久久好好热精品国产| 久久综合九色综合久99| 国产91色综合久久免费分享| 色婷婷久久久SWAG精品| 精品久久久久久久中文字幕 | 一本色道久久99一综合| 久久精品国产亚洲精品| 久久精品国产亚洲欧美| 老色鬼久久亚洲AV综合| 国内精品综合久久久40p| 久久久久亚洲精品日久生情| 国产欧美久久久精品影院| 麻豆久久久9性大片| 波多野结衣久久一区二区| 性做久久久久久免费观看| 久久频这里精品99香蕉久| 精品人妻伦九区久久AAA片69| 香蕉99久久国产综合精品宅男自 | 高清免费久久午夜精品|