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

            清風(fēng)竹林

            ぷ雪飄絳梅映殘紅
               ぷ花舞霜飛映蒼松
                 ----- Do more,suffer less

            Solmyr 的小品文系列之二:模棱兩可的陷阱

            為什么會這樣?!”,zero 一邊喝水一邊嘟囔著,恨恨的看著面前顯示器上的代碼,“為什么這么簡單的一個調(diào)用也會出現(xiàn)編譯錯誤 …… ”

            這是因為你的設(shè)計太差!”

            噗!zero 被幽靈一樣出現(xiàn)在背后的 Solmyr 嚇了一大跳,一口水差點全噴出來。

            咳!咳咳!S …… Solmyr ,你什么時候站在我背后的?”,zero 很費力的平息了咳嗽,同時努力回想剛才自己有沒有把柄會被 Solmyr 抓到。

            Solmyr
            抓過一張椅子坐了下來:“在你一開始干傻事的時候我就在了,正是這個糟糕的設(shè)計導(dǎo)致了現(xiàn)在困擾你的編譯錯誤。”

            哪 …… 哪里?”

            這兒。” Solmyr 抓過鍵盤,標(biāo)出了下面這段代碼:

            void SomeFunc(int i)
            …………

            void SomeFunc(float f)
            …………

            int main(void)
            {
                …………
                SomeFunc(1.2);         // Error! ambiguous call
               …………
            }

            我 也正覺得奇怪”,zero 一如既往的撓著頭,試圖壓榨不存在的智慧,“這么簡單的一個函數(shù)重載,應(yīng)該很清楚才對。我這里調(diào)用時明明給出的是浮點數(shù),顯然應(yīng)該調(diào)用 float 版本的 SomeFunc 。最奇怪的是如果沒有這個調(diào)用,整個程序編譯連接完全沒有問題,可見這樣重載函數(shù)是合法的。”

            嗯,沒錯,確實是合法的,但是合法不代表正確。zero ,你念一下這一段,看看先知 Meyers 在他的《50 》(注:指《Effective C++ 2/e》一書)中的條款 26 中是怎樣描述 C++ 對待‘模棱兩可’的哲學(xué)的。”,Solmyr 翻開了一本書,指著其中的幾行。

            “C++ ……”

            站起來,大聲念!”

            zero
            依言站起,中氣十足的念道:“C++ 也有一個哲學(xué)信仰:它相信潛在的模棱兩可的狀態(tài)不是一種錯誤。”

            旁邊的座位上傳來低低的竊笑聲,更遠(yuǎn)處的人探頭張望,投來好奇的目光,zero 頓時感到自己像個傻瓜。當(dāng) zero 看到 Solmyr 嘴邊招牌式的壞笑時明白了過來:自己又一次被 Solmyr 設(shè)計了。

            嗯,明白了這一點,我們就可以展開進(jìn)一步的討論了”,Solmyr 開始轉(zhuǎn)入正題,“還記得上次我說過上面的 1.2 是什么嗎?”

            zero
            露出了回憶的表情:“嗯 …… 1.2 是‘寫在代碼里的常量’…… 應(yīng)該是一個 double 類型常量。”

            這就是問題所在:編譯器看到這個調(diào)用函數(shù)的請求,會去尋找你的重載函數(shù)中哪個函數(shù)能夠匹配這個調(diào)用請求給出的參數(shù),結(jié)果它發(fā)現(xiàn)沒有一個函數(shù)的參數(shù)是 double 類型的,所以必須要做類型轉(zhuǎn)換,但是 double 類型既可以轉(zhuǎn)成 int ,也可以轉(zhuǎn)成 float ,究竟轉(zhuǎn)哪個好呢?編譯器不知道,所以只好報錯了。明白了嗎?”

            zero
            似懂非懂的點了點頭。

            那我問你,這樣重載編譯時會不會報錯?”,Solmyr 稍稍改動了一下 zero 的代碼:

            void SomeFunc(int i)
            …………

            void SomeFunc(double db)
            …………

            int main()
            {
               …………
                float f = 1.2;
                SomeFunc(f);
                …………
            }

            zero
            看了看,學(xué)著 Solmyr 的語氣說到:“編譯器發(fā)現(xiàn)沒有一個函數(shù)的參數(shù)是 float 類型的,所以必須要做類型轉(zhuǎn)換,但是 float 類型既可以轉(zhuǎn)成 int ,也可以轉(zhuǎn)成 double ,究竟轉(zhuǎn)哪個好呢?編譯器不知道,所以只好報錯了。”

            錯!”,Solmyr 順手按下了運行按鈕,程序運行一切正常,輸出顯示調(diào)用的是 double 版本的 SomeFunc 函數(shù)。

            zero
            再度感到了困惑:“為什么同樣是要選擇類型轉(zhuǎn)換,這個就沒錯,前一個就有錯呢?這中間的邏輯何在?”

            重要的是這一句:‘究竟轉(zhuǎn)哪個好呢?編譯器不知道’。你沒有注意到我說這句話的時候‘好’字上用了一個重音嗎?”

            你用過重音嗎?”

            “ ……
            這個不是重點。重點在于,float int float double 這兩個轉(zhuǎn)換,編譯器是能夠選擇的,因為 float int 會損失數(shù)據(jù) —— 象樣的編譯器會在做這種類型轉(zhuǎn)換的時候給出一個 warning —— float double 則不損失數(shù)據(jù),所以編譯器知道‘轉(zhuǎn)哪個好’。而之前的情況,double int float 的轉(zhuǎn)換都要損失數(shù)據(jù),所以編譯器不知道‘轉(zhuǎn)哪個好’,它沒辦法做一個決定 —— ”,Solmyr 看了看 zero ,再度問道,“明白了嗎?”

            zero
            皺著眉頭,撓頭撓的更起勁了,顯然對于消化一下子出現(xiàn)的這么多信息感到少許困難:“我想我明白了,關(guān)鍵是編譯器能否區(qū)分兩個類型轉(zhuǎn)換。在這里區(qū)分的關(guān)鍵是 類型轉(zhuǎn)換是否損失數(shù)據(jù),嗯 …… 所以我只要在所有用到浮點數(shù)的場合都使用 double 類型,就不會有問題,即使別人用 float 來調(diào)用也一樣。”

            正確。不過‘模棱兩可’的問題可不僅僅出現(xiàn)浮點數(shù)身上,例如,這樣兩個重載函數(shù) …… ”,Solmyr 接著鍵入:

            void SomeFunc(double db)
            void SomeFunc(char ch)

            如果我用一個整形變量來調(diào)用,會出現(xiàn)什么事情?”,Solmyr 扭頭盯著 zero

            呃 …… 編譯器同樣無法區(qū)分 int double int char 這兩個類型轉(zhuǎn)換,所以同樣會報錯。”

            正確。你能夠自己舉出幾個例子嗎?”,Solmyr 把鍵盤遞了回去。

            很明顯的,zero 陷入了沉思,過了一會兒,屏幕上出現(xiàn)了這樣幾行代碼:

            //
            int 調(diào)用的話會出錯
            void fun(char ch)
            void fun(int* pi)            //
            或者其他指針

            //
            int 調(diào)用同樣會出錯
            void fun(double db)
            void fun(int* pi)             //
            或者其他指針

            嗯,很好。不過你還是漏了一種重要情況,”,Solmyr 補(bǔ)充道,“就是參數(shù)有缺省值的時候:”

            //
            調(diào)用時如果不給參數(shù)會出錯
            void fun(int i=10)
            void fun()

            天哪!”,zero 看起來快要崩潰了,“居然有這么多模棱兩可的陷阱,這叫我怎樣發(fā)布我的函數(shù)?在文檔里寫:以下 153 種調(diào)用方式將導(dǎo)致編譯錯誤嗎?”

            不要這么緊張,”,Solmyr 好整以暇的說到,“重載函數(shù)的模棱兩可現(xiàn)象不是不能避免的,辦法有兩個:一是用模板來代替重載,尤其是象你的 SomeFunc 這樣 int 型和 double 型處理算法相同的情況;二是如果要用重載的話,盡可能保證函數(shù)的參數(shù)個數(shù)不同。”

            可是如果處理算法不一樣,函數(shù)需要的參數(shù)個數(shù)又相同,那該怎么辦?”

            很簡單,加入‘無用的參數(shù)’,象這樣:”

            void SomeFunc(float db, int)
            void SomeFunc(int i)

            第一個函數(shù)的第二個參數(shù)沒有任何作用,所以你可以干脆不給它命名,只要聲明一下有這個 int 型參數(shù)就可以了。文檔里可以這樣寫:該參數(shù)是為今后升級預(yù)留的余地,調(diào)用時請傳入 0 值。”

            “ ……
            你的文檔里大概都是這一類的話吧 …… 啊!好痛!這回又是一本書!”,zero Solmyr 突如其來的襲擊擊中,發(fā)出了悲慘的哀鳴。

            你得感謝先知 Scott Meyers,他的《 50 》輕而薄,我手上拿的若是一本教主 Bjarne Stroustrup 的《圣經(jīng)》(注:指《The C++ Programing Language 3/e》一書,Bjarne Stroustrup C++ 語言的設(shè)計者),你現(xiàn)在已經(jīng)爬不起來了。”,Solmyr 再度披上了修養(yǎng)的偽裝,不過言辭中仍然留著一點點殺氣的痕跡 ……

            真是殘暴的家伙 ……”,zero 小聲嘟囔著。

            你說什么?”,殺氣再度升高。

            不, 不!我什么也沒說!”,zero 連忙否認(rèn),試著轉(zhuǎn)移話題,“啊!我懂了,要避免模棱兩可的陷阱,一是用模板來替代重載,二是利用加入‘無用的參數(shù)’這一手段保證重載函數(shù)參數(shù)個數(shù)不同。這 樣就可以避開模棱兩可的問題,是不是,Solmyr 老師?”。zero 很努力的裝出天真無邪的樣子。

            “ ……
            真是拙劣的演技 …… ”,Solmyr 心中暗想。“不完全,上述手段只能解決函數(shù)重載這一塊而已,模棱兩可問題涉及的情況要廣泛的多,比如《 50 誡》中的例子:”

            class B;              //
            前置聲明

            class A
            {
            public:
                A(const B&);   // A
            可以根據(jù) B 構(gòu)造出來
            };

            class B
            {
            public:
                 operator A() const;    // B
            可以被轉(zhuǎn)換為 A
            };

            這兩個類本身沒有什么問題,但若是有個函數(shù)需要 A 的對象作為參數(shù),傳過去的卻是個 B 的對象時:”

            void f(const A&)
            B b;
            f(b);      // Error! ambiguous call

            注 意到這里面的問題了嗎?有兩種一樣好方法可以完成轉(zhuǎn)換,一是用 A 的構(gòu)造函數(shù)以 b 為參數(shù)構(gòu)造一個新的 A 類對象,而是調(diào)用 B 的轉(zhuǎn)換函數(shù)將 b 轉(zhuǎn)換為一個 A 類對象。編譯器再度無法區(qū)分哪個轉(zhuǎn)換更好,只能報錯了。后面還有一個多重繼承的例子,你自己看吧”

            這 …… 這 ……”,zero 剛剛建立起來的對回避陷阱的自信再度崩塌。

            要回避一切模棱兩可的問題是不可能的,”,Solmyr 站起身來,“關(guān)鍵是了解它為什么會發(fā)生,怎樣的情況容易誘發(fā)它,然后小心的加以處理,C++ 中很多問題都是如此。這塊《 50 誡》的石板就留給你了,好好研讀吧。哈哈哈哈!”,Solmyr 一邊笑著一邊離開了 zero ,背影看起來像是一位飄然遠(yuǎn)去的高人 ……

            什么呀!根本就只是一個性格殘暴的家伙而已,裝模做樣 …… 啪!”,zero 話音未落,一個文件夾劃破空氣飛來,正中 zero 的面門。

            嗚 ~ 我什么也沒說 ~”,zero 無力的辨白,然而換來的只是旁邊的座位上再度傳來低低的竊笑聲而已。zero 明白,今天他的形象算是徹底的毀了 ……

            posted on 2009-08-19 10:39 李現(xiàn)民 閱讀(762) 評論(1)  編輯 收藏 引用 所屬分類: 絕對盜版

            評論

            # re: Solmyr 的小品文系列之二:模棱兩可的陷阱 2009-09-16 13:56 egmkang.wang

            寫一個T版的吧  回復(fù)  更多評論   

            一本色道久久88综合日韩精品| 精品国产乱码久久久久软件| 99久久精品免费| 伊人久久大香线蕉精品| 无码人妻久久一区二区三区蜜桃| 欧美精品国产综合久久| 99精品伊人久久久大香线蕉| 午夜精品久久久久久| 久久婷婷成人综合色综合| 久久精品中文字幕第23页| 久久久久亚洲AV无码观看| 久久精品无码一区二区三区免费 | 国内精品久久久久久久亚洲| 久久天天躁狠狠躁夜夜不卡| 91精品无码久久久久久五月天| 国内精品久久久久影院薰衣草 | 久久婷婷国产麻豆91天堂| 欧美精品九九99久久在观看| 狠狠色丁香久久综合五月| 日韩AV无码久久一区二区| 久久久久亚洲AV综合波多野结衣 | 久久国产一片免费观看| 久久国产色AV免费观看| 亚洲日本va午夜中文字幕久久| 久久国产精品久久国产精品| 中文字幕无码精品亚洲资源网久久| 久久久久99精品成人片| 精品视频久久久久| 一本一本久久a久久综合精品蜜桃| 久久精品国产99久久久香蕉| 国产精品久久久久久福利漫画 | 久久精品无码一区二区WWW| 国产亚洲成人久久| 日韩精品国产自在久久现线拍| 日产精品久久久久久久| 亚洲精品tv久久久久久久久| yy6080久久| 久久精品国产99久久久| 大伊人青草狠狠久久| 日本福利片国产午夜久久| 国产精品丝袜久久久久久不卡|