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

            常用鏈接

            留言簿(10)

            我參與的團(tuán)隊(duì)

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

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

            噢,我親愛的++

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

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

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

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

             

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

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

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

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

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

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

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

            然后,面積計(jì)算公式稍作改動(dòng)就行了:

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

            這時(shí),如果我改變了Rectangle的數(shù)據(jù)存儲(chǔ)方式,也不會(huì)影響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;
            };

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

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

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


            作為一個(gè)思想純正的OOP程序員而言,這是一個(gè)漂亮的設(shè)計(jì)。不過(guò),對(duì)于我這樣一個(gè)同樣思想純正的Multiple-paradigm程序員而言,這是個(gè)不恰當(dāng)?shù)脑O(shè)計(jì)。

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

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

            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ù)通過(guò)get_center_x()等成員函數(shù)計(jì)算獲得:

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

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

            這還只是小問(wèn)題。更重要的是增加了這些冗余的函數(shù),使得類在接口的靈活性上變差。假設(shè)我們?cè)?/span>Cycle類上增加一個(gè)offset()函數(shù),實(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è),此時(shí)來(lái)了一個(gè)需求,要求offset()可以接受size類的對(duì)象作為參數(shù)。那么就必須修改Cycle類的定義,改變或重載offset()。如果這個(gè)Cycle是別人寫的,不是我們所能改變的,那么事情就比較麻煩。

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

            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í),如果需求改變,那么只需編寫一個(gè)函數(shù)重載,便可以解決問(wèn)題,而無(wú)需考慮類的修改了。

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

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

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

            面對(duì)這些圓的實(shí)現(xiàn),為它們各自開發(fā)一套算法實(shí)在讓人泄氣。大量的重復(fù)代碼,和重復(fù)勞動(dòng),簡(jiǎn)直是對(duì)程序員的智慧的侮辱。我們需要開發(fā)一套算法,然后用于所有圓類。這就需要?jiǎng)佑?/span>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);
            }

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

            CycleA c1;
            CycleB c2;
            CycleC c3;

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

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

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

            怎么辦?設(shè)計(jì)模式告訴我們,可以用Adapter解決問(wèn)題:

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

            唉,世事難料,上頭下命令,必須同時(shí)使用我們自己的圓類和頑固派的圓類。(肯定是收了他們的好處了)。沒(méi)辦法,命令終究是命令??蓮慕裢?,我們就得同時(shí)開發(fā)兩套算法。痛苦。不過(guò)相比使用算法的人來(lái)說(shuō),我們還算幸運(yùn)的。他們必須不斷地在OursTheirs命名空間里跳來(lái)跳去,時(shí)間長(zhǎng)了難保不出錯(cuò)。

            算法使用者希望一個(gè)算法就是一個(gè)名字,在同一個(gè)命名空間,以免混亂。幸運(yùn)的是,在一種未來(lái)技術(shù)的支持下,我們做到了。這就是C++BM(彈道導(dǎo)彈,MDW的運(yùn)載器)——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ā)展,我們不僅僅需要操作一個(gè)圖形,還要把它畫出來(lái)。這件事不算難。但是,面對(duì)不同的需求,我們有完全不同的兩套方案。

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

            定義一個(gè)抽象類:

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

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

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

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

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

            Cycle c;
            c.Draw(dc);

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

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

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

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

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

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

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

            作為獨(dú)立于OOP的一種新的(其實(shí)也不怎么新,其理論根源可以追溯到1967年以前)范式,模板(泛型)相關(guān)的編程被稱為“泛型編程”(GP)。gp最常用的一種風(fēng)格就是算法獨(dú)立于類,這在前面我們已經(jīng)看到過(guò)了。所以,這里的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)用不同的圖形對(duì)象調(diào)用Draw時(shí),編譯器會(huì)自動(dòng)匹配不同的版本:

            Cycle c;
            Rectangle r;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            此后,可以很簡(jiǎn)單地處理dcRestore問(wèn)題:

            void Draw(CDC& dc, Cycle& c) {
               StoreDC    sd(dc);
               …  
            //使用dc,可能拋異常
            }  //函數(shù)結(jié)束時(shí),會(huì)自動(dòng)RestoreDC,無(wú)論正常退出還是拋出異常

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

             

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

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

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

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

            Feedback

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

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

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

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

            厲害啊



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

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

            不錯(cuò),受教了。

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

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

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

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

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

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


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


            亚洲&#228;v永久无码精品天堂久久 | 国产精品久久久久影院色| 久久久久国产日韩精品网站| 久久伊人色| 成人久久免费网站| 精品久久久噜噜噜久久久| 免费观看成人久久网免费观看| 久久99国产精一区二区三区| 999久久久免费国产精品播放| 亚洲人成无码久久电影网站| 久久乐国产综合亚洲精品| 久久人人爽人人爽人人片av麻烦| 中文精品久久久久人妻不卡| 囯产极品美女高潮无套久久久| 国产69精品久久久久777| 人妻中文久久久久| 亚洲国产精品无码久久久不卡| 久久精品中文闷骚内射| 久久精品免费观看| 久久久久久精品免费看SSS| 色综合合久久天天综合绕视看| 久久热这里只有精品在线观看| 国产精品一久久香蕉产线看| 亚洲性久久久影院| 国产成人精品久久亚洲高清不卡 | 中文字幕乱码久久午夜| 久久精品亚洲男人的天堂 | 亚洲欧美日韩久久精品第一区| 色综合久久中文综合网| 精品国产乱码久久久久久郑州公司| 欧美激情精品久久久久久| 国产精品久久波多野结衣| 性欧美大战久久久久久久久| 亚洲欧美成人久久综合中文网 | 思思久久99热只有频精品66| a高清免费毛片久久| 久久永久免费人妻精品下载| 亚洲国产精品无码久久| 伊人久久精品影院| 久久亚洲国产最新网站| 久久无码人妻精品一区二区三区|