• <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>
            posts - 34,comments - 2,trackbacks - 0
            轉(zhuǎn)載自:http://blog.csdn.net/norains/archive/2009/07/21/4366530.aspx

                #define,const,enum:這三者有何關(guān)聯(lián)?一個是宏定義,一個是靜態(tài)修飾符,最后一個還是枚舉類型。是不是有點(diǎn)像養(yǎng)麥皮打漿糊——粘不到一 起?如果我們將范圍縮小再縮小,讓三者都只局限于“固定值”,那么千絲萬縷的關(guān)系就了然于紙上——至少,有共同點(diǎn)了。

            在解釋什么是“固定值”之前,我們先來了解何為“奇數(shù)”。太多的原則都有告誡,少用“奇數(shù)”,因為這將導(dǎo)致代碼不可維護(hù)。聽起來似乎如算命的釋語般玄之又玄,不可捉摸,但其間的語義卻是如此簡單。下面這兩個代碼段,正好說明“奇數(shù)”之糟糕:

            1. 代碼段1:      
            2. switch(mode)  
            3. {  
            4.   case 1:  
            5.    //TO Do someting.  
            6.    break;         
            7.   case 2:  
            8.    //TO Do someting.  
            9.    break;         
            10.   case 3:  
            11.    //TO Do someting.  
            12.    break;  
            13. }  
            14.   
            15. 代碼段2:  
            16. switch(mode)  
            17. {  
            18.   case SLEEP:  
            19.    //TO Do someting.  
            20.    break;         
            21.   case POWER_OFF:  
            22.    //TO Do someting.  
            23.    break;         
            24.   case POWER_ON:  
            25.    //TO Do someting.  
            26.    break;  
            27. }  
            代碼段1: switch(mode) { case 1: //TO Do someting. break; case 2: //TO Do someting. break; case 3: //TO Do someting. break; } 代碼段2: switch(mode) { case SLEEP: //TO Do someting. break; case POWER_OFF: //TO Do someting. break; case POWER_ON: //TO Do someting. break; }

                顯而易見,代碼段2的可讀性比代碼段1要高多了。在這兩個實例里,像“1”,“2”,“3”這種就叫奇數(shù),而 “SLEEP”,“POWER_OFF”,“POWER_ON”就是固定值。固定值的定義在C++中有三種方式,分別就是本文要討論 的#define,const和enum。

            大名鼎鼎的《Effect C++》的作者Scott Meyers就曾建議過,凡是用const能代替#define的地方,都應(yīng)該用const。這句話不無道理,也從另一方面來說,#define和const事實上很多地方都能互用。

            比如

            1. const DWORD DEFAULT_VOLUME = 0xFFFF;  
            2. //#define DEFAULT_VOLUME    0xFFFF  
            3.   
            4. ...  
            5.   
            6. m_dwVolume = DEFAULT_VOLUME;  
            const DWORD DEFAULT_VOLUME = 0xFFFF; //#define DEFAULT_VOLUME 0xFFFF ... m_dwVolume = DEFAULT_VOLUME;

                無論你是用const還是#define來定義DEFAULT_VOLUME,對于m_dwVolume = DEFAULT_VOLUME這語句而言都沒有本質(zhì)性的變化。那么,是不是意味著,是用#define還是用const,完全取決于當(dāng)時的心情了?答案自 然是否定的,否則本文就成了抒情散文了。

            #define有個致命的缺陷,不受作用域限制。凡是在#define之后的代碼,都可以直接使用#define定義的數(shù)值。

            我們經(jīng)常會寫這么一個函數(shù),用以獲取某個設(shè)備的DWORD值。但這個函數(shù)不是返回BOOL類型來表示成敗,而是采用另外一種方式:當(dāng)讀取成功時,返回的是 具體和設(shè)備有關(guān)的數(shù)值;當(dāng)失敗時,返回的是默認(rèn)數(shù)值。聽起來這函數(shù)功能有點(diǎn)奇怪,也懷疑在什么情況下才會采用如此設(shè)計,但可惜本文主題不是討論該函數(shù)能干 什么,或應(yīng)該出現(xiàn)于什么地點(diǎn),我們只要知道有這么一種函數(shù)即可。

            我們姑且假設(shè)這函數(shù)原型如下:

            1. DWORD GetDevDW(HANDLE hDev,DWORD dwError);  
            DWORD GetDevDW(HANDLE hDev,DWORD dwError);

                調(diào)用也很簡單:

            1. DWORD dwVal = GetDevDW(hDev,ERROR_VALUE);  
            DWORD dwVal = GetDevDW(hDev,ERROR_VALUE);


            在這個例子中,如果dwVal的數(shù)值等于ERROR_VALUE,那么意味著調(diào)用GetDevDW失敗;不等于ERROR_VALUE才意味著調(diào)用成功。

            現(xiàn)在我們有兩個函數(shù),分別用來獲取兩個設(shè)備的信息。在接下來的例子中,我們采用#define來定義固定值:

            1. void GetDev1Info()  
            2. {  
            3.   ....  
            4.     
            5.     #define ERROR_VALUE 0  
            6.     GetDevDW(NULL,ERROR_VALUE);  
            7.       
            8.     ...  
            9. }  
            10.   
            11. void GetDev2Info()  
            12. {  
            13.   ....  
            14.     
            15.     #define ERROR_VALUE 2  
            16.     GetDevDW(NULL,ERROR_VALUE);  
            17.       
            18.     ...  
            19. }  
            void GetDev1Info() { .... #define ERROR_VALUE 0 GetDevDW(NULL,ERROR_VALUE); ... } void GetDev2Info() { .... #define ERROR_VALUE 2 GetDevDW(NULL,ERROR_VALUE); ... }

                看起來一切似乎都挺好,難道不是嘛?只可惜,編譯會有警告出現(xiàn):'ERROR_VALUE' : macro redefinition。

            問題的根源只在于#define的數(shù)值沒有作用域的概念。更為糟糕的是,在GetDev2Info函數(shù)中使用的ERROR_VALUE并不是我們所期望的2,而是在GetDev1Info中定義的0。噢,我的天,再也沒有比這更糟糕的事了。

            為了徹底解決這個警告,我們可以在GetDev2Info函數(shù)做一些額外的工作:

            1. void GetDev2Info()  
            2. {  
            3.   ....  
            4.     
            5.   #ifdef ERROR_VALUE  
            6.     #undef ERROR_VALUE  
            7.   #endif  
            8.     
            9.     #define ERROR_VALUE 2  
            10.     GetDevDW(NULL,ERROR_VALUE);  
            11.       
            12.     ...  
            13. }  
            void GetDev2Info() { .... #ifdef ERROR_VALUE #undef ERROR_VALUE #endif #define ERROR_VALUE 2 GetDevDW(NULL,ERROR_VALUE); ... }

                問題解決了,警告沒有了,但代碼卻丑陋了。

            還有另一種方式,更改固定值的名稱:

            1. void GetDev1Info()  
            2. {  
            3.   ....  
            4.     
            5.     #define DEV1_ERROR_VALUE 0  
            6.     GetDevDW(NULL,DEV1_ERROR_VALUE);  
            7.       
            8.     ...  
            9. }  
            10.   
            11. void GetDev2Info()  
            12. {  
            13.   ....  
            14.     
            15.     #define DEV2_ERROR_VALUE 2  
            16.     GetDevDW(NULL,DEV2_ERROR_VALUE);  
            17.       
            18.     ...  
            19. }  
            void GetDev1Info() { .... #define DEV1_ERROR_VALUE 0 GetDevDW(NULL,DEV1_ERROR_VALUE); ... } void GetDev2Info() { .... #define DEV2_ERROR_VALUE 2 GetDevDW(NULL,DEV2_ERROR_VALUE); ... }

                同樣,問題解決了,警告沒有了,并且,代碼也不算丑陋。遺留的唯一問題是,如果類似函數(shù)很多的話,我們需要絞盡腦汁去給每個錯誤固定值選擇一個唯一的名字。呃,這對于我們這些懶人而言,這并不算一個好差事。既然如此,為什么不用const呢?

            1. void GetDev1Info()  
            2. {  
            3.   ...  
            4.     
            5.     const DWORD ERROR_VALUE = 0;  
            6.     GetDevDW(NULL,ERROR_VALUE);  
            7.       
            8.     ....  
            9. }  
            10.   
            11. void GetDev2Info()  
            12. {  
            13.   ...  
            14.     
            15.     const DWORD ERROR_VALUE = 2;  
            16.     GetDevDW(NULL,ERROR_VALUE);  
            17.       
            18.     ...  
            19. }  
            void GetDev1Info() { ... const DWORD ERROR_VALUE = 0; GetDevDW(NULL,ERROR_VALUE); .... } void GetDev2Info() { ... const DWORD ERROR_VALUE = 2; GetDevDW(NULL,ERROR_VALUE); ... }

                沒錯,僅此而已。因為const DWORD聲明的是一個局部變量,受限于作用域的局限,所以我們在GetDev1Info和GetDev2Info都能使用相同的固定值名稱。

            這個例子也許還不足以說服你用const替代#define,那么接下來的例子你應(yīng)該會扭轉(zhuǎn)這一觀念——或許這例子你已經(jīng)碰到過。

            我們有兩個class,分別用來控制汽車的重音和功放。這兩個類都需要在頭文件中定義MAX_VOLUME以供使用者調(diào)用,但很不幸的是,重音和功放的MAX_VOLUME值是不同的。

            如果用#define,在頭文件中我們可能這么寫:

            1. ///////////////////////////////////  
            2. //Bass.h  
            3. #define MAX_VOLUME 15  
            /////////////////////////////////// //Bass.h #define MAX_VOLUME 15
            1. ///////////////////////////////////  
            2. //Amplifier.h  
            3. #define MAX_VOLUME 30  
            /////////////////////////////////// //Amplifier.h #define MAX_VOLUME 30

                當(dāng)兩個頭文件沒有同時使用時,一切都很順利,不是嘛?

            但如果我需要同時控制著兩個音量,那么我們就必須要同時include這兩個文件,像這種調(diào)用大家應(yīng)該不陌生吧:

            1. #include "Bass.h"  
            2. #include "Amplifier.h"  
            #include "Bass.h" #include "Amplifier.h"

                那么問題就很顯然:嚴(yán)重的警告或是無法通過編譯。

            為了解決這個問題,我們還是只能請出const。只不過,如果還是簡單地聲明如下:

            1. ///////////////////////////////////  
            2. //Bass.h  
            3. const DWORD MAX_VOLUME = 15;  
            /////////////////////////////////// //Bass.h const DWORD MAX_VOLUME = 15;
            1. ///////////////////////////////////  
            2. //Amplifier.h  
            3. const DWORD MAX_VOLUME = 30;  
            /////////////////////////////////// //Amplifier.h const DWORD MAX_VOLUME = 30;

                那么該出現(xiàn)的問題還是和用#define一樣,沒有任何本質(zhì)上的改變。這時候,我們只能請出namespace了。

            1. ///////////////////////////////////  
            2. //Bass.h  
            3. namespace Bass  
            4. {  
            5.  const DWORD MAX_VOLUME = 15;  
            6. };
            1. ///////////////////////////////////  
            2. //Amplifier.h  
            3. namespace Amplifier  
            4. {  
            5.  const DWORD MAX_VOLUME = 30;  
            6. }  
            /////////////////////////////////// //Amplifier.h namespace Amplifier { const DWORD MAX_VOLUME = 30; }

                在沒有使用using來省略命名空間的情況下,我們可以這么折騰代碼:

            1. DWORD dwBass = Bass::MAX_VOLUME;  
            2. DWORD dwAmplifier = Amplifier::MAX_VOLUME;  
            DWORD dwBass = Bass::MAX_VOLUME; DWORD dwAmplifier = Amplifier::MAX_VOLUME;

                在這個例子中,命名空間起到標(biāo)志作用,標(biāo)明當(dāng)前的MAX_VOLUME屬于哪種范疇,也算意外的收獲。

            看到這里,也許有人會問,如果是namespace + #define方式可以么?很遺憾,答案是不行。正如前面所說,#define不受限于作用域,所以簡簡單單的namespace無法套住#define這只猛獸。

            至此,我們可以這么下定論,在不涉及到條件編譯,并且只是使用固定值的前提下,我們都應(yīng)該用const來替代#define。

            基于這個原則,以下的討論我們就拋開#define,只用const。

            我們再回過頭來看看文章最初的例子,將其封裝為一個函數(shù)

            1. BOOL SwitchMode(DWORD mode)  
            2. {  
            3.   ...  
            4.     
            5.   switch(mode)  
            6.   {  
            7.     case SLEEP:  
            8.      //TO Do someting.  
            9.      break;         
            10.     case POWER_OFF:  
            11.      //TO Do someting.  
            12.      break;         
            13.     case POWER_ON:  
            14.      //TO Do someting.  
            15.      break;  
            16.   }  
            17.     
            18.   ...        
            19. }  
            BOOL SwitchMode(DWORD mode) { ... switch(mode) { case SLEEP: //TO Do someting. break; case POWER_OFF: //TO Do someting. break; case POWER_ON: //TO Do someting. break; } ... }

                在代碼的他處定義了如下固定值:

            1. const DWORD SLEEP = 0x00;  
            2. const DWORD POWER_OFF = 0x02;  
            3. const DWORD POWER_ON = 0x03;  
            const DWORD SLEEP = 0x00; const DWORD POWER_OFF = 0x02; const DWORD POWER_ON = 0x03;

                調(diào)用的時候:

            1. SwitchMode(SLEEP);  
            2.   
            3. ...  
            4.   
            5. SwitchMode(POWER_OFF);  
            6.   
            7. ...  
            SwitchMode(SLEEP); ... SwitchMode(POWER_OFF); ...

                很好,很漂亮,難道不是么?

            但這樣子無法保證使用者不是如此調(diào)用代碼:

            1. SwitchMode(0x100);  
            SwitchMode(0x100);

                0x100不是我們想要的數(shù)值,在SwitchMode函數(shù)也不會對該數(shù)值有相應(yīng)的處理,但偏偏這符合編譯器的規(guī)范,它會讓這代碼沒有任何警告沒有任何錯誤順利編譯通過。

            也許還有人說,誰會那么傻,直接用0x100來賦值啊?這話確實沒錯,直接用0x100的概率確實太少了。

            但我們無法否認(rèn),會有這么一種可能:有另外一個函數(shù),其中一個固定值為如下定義:

            1. const DWORD FILE_MODE = 0x100;  
            const DWORD FILE_MODE = 0x100;

                而我們一時沖昏了頭,又或許喝醉了酒,將該參數(shù)誤用了:

            1. SwitchMode(FILE_MODE);  
            SwitchMode(FILE_MODE);

                對于編譯器來說,無論是0x100還是FILE_MODE,都沒有太多意義,所以這病態(tài)代碼很容易通過編譯器檢測;而對于人而言,因為已經(jīng)使用了固定值,也下意識以為這參數(shù)是符合的。兩者,無論是編譯器,還是我們,都被合理地蒙騙了。

            那么,我們有辦法在編譯的時候,如果該數(shù)值不是我們所想要的,編譯器能給使用者提示警告甚至錯誤么?

            一切皆有可能!不過,這時候我們不能使用const,而必須換用enum。

            首先用enum定義固定值:

            1. enum Mode  
            2. {  
            3.     SLEEP,  
            4.     POWER_OFF,  
            5.     POWER_ON,  
            6. };  
            enum Mode { SLEEP, POWER_OFF, POWER_ON, };

                函數(shù)的聲明如此更換:

            1. BOOL SwitchMode(Mode mode)  
            BOOL SwitchMode(Mode mode)

                調(diào)用也是和之前無異:

            1. SwitchMode(SLEEP);  
            2.   
            3. ...  
            4.   
            5. SwitchMode(POWER_OFF);  
            6.   
            7. ...  
            SwitchMode(SLEEP); ... SwitchMode(POWER_OFF); ...

                唯一的不同就是,如果你這樣調(diào)用:

            1. SwitchMode(0x100); //這時候無法編譯通過  
            2. SwitchMode(FILE_MODE); //這時候無法編譯通過  
            SwitchMode(0x100); //這時候無法編譯通過 SwitchMode(FILE_MODE); //這時候無法編譯通過

                那么編譯器就會毫不猶豫地發(fā)出抱怨:cannot convert parameter 1 from 'int' to 'Mode'。

            很好,編譯器已經(jīng)作為我們的第一道防火墻,將我們所不需要的毫無關(guān)聯(lián)的數(shù)值通通排除在外。難道不是很美好嗎?

            當(dāng)然,如果你想強(qiáng)制讓編譯器通過異樣的數(shù)值也不是不可能

            1. SwitchMode(static_cast<Mode>(0x100));   
            SwitchMode(static_cast<Mode>(0x100));

                雖然0x100不處于Mode的范圍之內(nèi),但依然還是通過了編譯器的檢測。對此,我們毫無辦法。只是,像這種極端的異教徒的做法,有多少情況下會碰到呢?


            最后的最后,我們略微總結(jié)一下:

            1.只是聲明單一固定值,盡可能采用const。

            2.如果是一組固定值,并且互相有關(guān)聯(lián),則采用enum。

            3.不涉及條件編譯,只是定義固定值的情形下,盡可能不使用#define。

            posted on 2011-08-19 17:48 Yu_ 閱讀(417) 評論(0)  編輯 收藏 引用 所屬分類: 數(shù)據(jù)結(jié)構(gòu)與C++語法

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


            久久精品草草草| 久久久久国产| 欧美一区二区精品久久| 久久久久免费精品国产| 久久久久亚洲AV无码去区首| 日产精品久久久久久久| 久久久99精品成人片中文字幕| 久久天天婷婷五月俺也去| 国产精品18久久久久久vr| 久久精品国产亚洲AV香蕉| 久久精品国产亚洲一区二区| 久久综合鬼色88久久精品综合自在自线噜噜 | 国产精品99久久99久久久| 青青国产成人久久91网| 精品久久久无码人妻中文字幕| 99久久国产亚洲高清观看2024| 综合久久国产九一剧情麻豆| 狠狠久久综合| 久久国产视屏| 大香网伊人久久综合网2020| av无码久久久久久不卡网站| 狠狠色综合网站久久久久久久高清| 久久综合狠狠综合久久97色| 99久久免费国产精品热| 91久久婷婷国产综合精品青草 | 99精品久久久久中文字幕| 久久一日本道色综合久久| 无码日韩人妻精品久久蜜桃| 久久婷婷色香五月综合激情| 一本一道久久a久久精品综合| 久久精品国产99国产精品| 国产精品免费久久久久影院| 亚洲国产天堂久久综合网站| 日韩欧美亚洲综合久久影院d3| 国产精品无码久久久久久| 久久亚洲中文字幕精品有坂深雪 | 久久精品国产第一区二区三区| 亚洲欧美日韩中文久久| 国产成年无码久久久久毛片| 国产精品久久久久jk制服| 久久精品草草草|