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

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

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

            噢,我親愛的++

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

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

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

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

             

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

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

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

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

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

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

            struct Cycle
            {
               
            float   center_x;
               
            float   center_y;
               
            float   radius;
            };
                    很傳統(tǒng)的表示,<圓心坐標(biāo),半徑>,便可以立刻定義出一個圓形?,F(xiàn)在,假設(shè)我們需要計算圓形的面積。于是,我寫了一個函數(shù)執(zhí)行這項任務(wù):
            float Area(const Cycle & rc) {
               
            return  PI*rc.radius*rc.radius;
            }
                很好。但是突然有一天,我心血來潮,把圓形類的存儲改成外切正方形的<左上角,右下角>形式,那么這個函數(shù)就不能用了。為了讓我這么一個三心二意的人能夠得到滿足,就得運用封裝這個特性了:
            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的數(shù)據(jù)存儲方式,也不會影響Area函數(shù):

            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;
            };

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

            當(dāng)我們嘗到封裝的甜頭之后,便會繼續(xù)發(fā)揚光大:

            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程序員而言,這是一個漂亮的設(shè)計。不過,對于我這樣一個同樣思想純正的Multiple-paradigm程序員而言,這是個不恰當(dāng)?shù)脑O(shè)計。

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

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

            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;
            };

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

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

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

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

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

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

            Cycle c;

            c.offset(
            20100);

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

            按照現(xiàn)代的Multiple-paradigm的設(shè)計理念,這類操作應(yīng)當(dāng)以non-member non-friend的形式出現(xiàn),而類僅僅保持最小的、無冗余的接口集合:

            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);
            }

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

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

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

            經(jīng)過長時間的開發(fā)工作,我們逐步積累起很多圓類,都是面向不同實現(xiàn)。有的通過傳統(tǒng)的<圓心,半徑>存放數(shù)據(jù);有的通過外接正方形坐標(biāo)保存數(shù)據(jù);有的通過一個長軸等于短軸的橢圓存放數(shù)據(jù);有的通過內(nèi)接正方形保存數(shù)據(jù);。不過它們的接口都是相同的,即<圓心,半徑>形式。

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

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

            不過,有些頑固的人認(rèn)定一個圓應(yīng)當(dāng)用外接正方形的形式定義(接口形式是外接正方形的坐標(biāo))。并且基于這種構(gòu)造,開發(fā)了一堆有用的函數(shù)模板。比如說inflate<>()

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

            怎么辦?設(shè)計模式告訴我們,可以用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;
            };

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

            Ours::Cycle c;

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

            唉,世事難料,上頭下命令,必須同時使用我們自己的圓類和頑固派的圓類。(肯定是收了他們的好處了)。沒辦法,命令終究是命令??蓮慕裢螅覀兙偷猛瑫r開發(fā)兩套算法。痛苦。不過相比使用算法的人來說,我們還算幸運的。他們必須不斷地在OursTheirs命名空間里跳來跳去,時間長了難保不出錯。

            算法使用者希望一個算法就是一個名字,在同一個命名空間,以免混亂。幸運的是,在一種未來技術(shù)的支持下,我們做到了。這就是C++BM(彈道導(dǎo)彈,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));     //調(diào)用#1
            move(c2, point(5111));   //調(diào)用#1
            move(c3, point(722));    //調(diào)用#2

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

            先看一下常見的方案——OOP。這是經(jīng)典的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) {
                   … 
            //繪制矩形
               }
            };

            此后,便可以創(chuàng)建一個對象并繪制:

            Cycle c;
            c.Draw(dc);

            但這同不用虛函數(shù)有什么區(qū)別?請看以下代碼:

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

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

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

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

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

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

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

            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));
            }

            當(dāng)用不同的圖形對象調(diào)用Draw時,編譯器會自動匹配不同的版本:

            Cycle c;
            Rectangle r;

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

            這里使用了函數(shù)模板特化這種特性,促使編譯器在編譯時即根據(jù)特化的情況調(diào)用合適的函數(shù)模板版本。不過,仔細看函數(shù)模板的聲明,會發(fā)現(xiàn)這同函數(shù)的重載幾乎一樣。實際上此時使用函數(shù)重載更加恰當(dāng)。(函數(shù)重載通常也被認(rèn)為是一種多態(tài))。這里使用模板,是為了引出未來的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也實現(xiàn)了編譯期的操作分派(以類型為tag)。此時,我們便可以看出,引入了concept之后,模板的特化(針對concept)不僅僅使得代碼重用率提高,而且其形式同函數(shù)重載更加類似。也就是說,重載多態(tài)和函數(shù)模板的靜多態(tài)有了相同的含義(語義),兩者趨向于統(tǒng)一。

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

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

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

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

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

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

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

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

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

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

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

            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,可能拋異常
            }  //函數(shù)結(jié)束時,會自動RestoreDC,無論正常退出還是拋出異常

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

             

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

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

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

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

            Feedback

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

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

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

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

            厲害啊



            歡迎來到國內(nèi)最好的壁紙論壇 5D壁紙 http://5d6p.5d6d.com  回復(fù)  更多評論
              

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

            不錯,受教了。

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

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

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

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

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

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


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


            狠狠干狠狠久久| 亚洲午夜无码AV毛片久久| 国产精品久久久久久福利漫画 | 精品国产乱码久久久久久郑州公司| 狠狠久久综合伊人不卡| 精品午夜久久福利大片| 青春久久| 中文国产成人精品久久亚洲精品AⅤ无码精品 | 国产精品丝袜久久久久久不卡 | 久久精品国产精品国产精品污| 久久午夜福利电影| 久久久WWW成人| 久久青青草原精品国产不卡| 麻豆一区二区99久久久久| 久久精品国产第一区二区三区 | 久久国产亚洲精品麻豆| 一本大道久久香蕉成人网| 精品久久一区二区| 久久久久女人精品毛片| 伊人色综合九久久天天蜜桃| 久久亚洲国产中v天仙www| 久久婷婷久久一区二区三区| 久久人做人爽一区二区三区| 色偷偷久久一区二区三区| 久久久亚洲精品蜜桃臀| 亚洲狠狠久久综合一区77777 | 久久中文字幕精品| 熟妇人妻久久中文字幕| 久久亚洲欧美国产精品| 久久国产欧美日韩精品免费| 亚洲欧美一级久久精品| 久久国产精品二国产精品| 欧美久久综合性欧美| jizzjizz国产精品久久| 久久久久亚洲av无码专区喷水| 久久香综合精品久久伊人| 久久狠狠爱亚洲综合影院| 久久WWW免费人成一看片| 国产成人精品久久| 久久久精品一区二区三区| 丰满少妇高潮惨叫久久久|