青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 34,comments - 2,trackbacks - 0
轉載自:http://blog.csdn.net/norains/archive/2009/07/21/4366530.aspx

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

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

  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”這種就叫奇數,而 “SLEEP”,“POWER_OFF”,“POWER_ON”就是固定值。固定值的定義在C++中有三種方式,分別就是本文要討論 的#define,const和enum。

大名鼎鼎的《Effect C++》的作者Scott Meyers就曾建議過,凡是用const能代替#define的地方,都應該用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這語句而言都沒有本質性的變化。那么,是不是意味著,是用#define還是用const,完全取決于當時的心情了?答案自 然是否定的,否則本文就成了抒情散文了。

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

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

我們姑且假設這函數原型如下:

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

    調用也很簡單:

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


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

現在我們有兩個函數,分別用來獲取兩個設備的信息。在接下來的例子中,我們采用#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); ... }

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

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

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

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

    同樣,問題解決了,警告沒有了,并且,代碼也不算丑陋。遺留的唯一問題是,如果類似函數很多的話,我們需要絞盡腦汁去給每個錯誤固定值選擇一個唯一的名字。呃,這對于我們這些懶人而言,這并不算一個好差事。既然如此,為什么不用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,那么接下來的例子你應該會扭轉這一觀念——或許這例子你已經碰到過。

我們有兩個class,分別用來控制汽車的重音和功放。這兩個類都需要在頭文件中定義MAX_VOLUME以供使用者調用,但很不幸的是,重音和功放的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

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

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

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

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

為了解決這個問題,我們還是只能請出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;

    那么該出現的問題還是和用#define一樣,沒有任何本質上的改變。這時候,我們只能請出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;

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

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

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

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

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

  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;

    調用的時候:

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

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

但這樣子無法保證使用者不是如此調用代碼:

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

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

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

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

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

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

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

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

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

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

首先用enum定義固定值:

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

    函數的聲明如此更換:

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

    調用也是和之前無異:

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

    唯一的不同就是,如果你這樣調用:

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

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

很好,編譯器已經作為我們的第一道防火墻,將我們所不需要的毫無關聯的數值通通排除在外。難道不是很美好嗎?

當然,如果你想強制讓編譯器通過異樣的數值也不是不可能

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

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


最后的最后,我們略微總結一下:

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

2.如果是一組固定值,并且互相有關聯,則采用enum。

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

posted on 2011-08-19 17:48 Yu_ 閱讀(426) 評論(0)  編輯 收藏 引用 所屬分類: 數據結構與C++語法

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            一区二区三区产品免费精品久久75 | 亚洲欧美日韩国产一区| 欧美在线播放| 99视频超级精品| 欧美精品在线一区| 国产精品视频一区二区高潮| 亚洲欧美三级伦理| 一道本一区二区| 欧美日韩高清在线| 一区二区三区四区五区视频| 亚洲黄色av| 麻豆精品传媒视频| 亚洲欧洲精品一区| 亚洲东热激情| 欧美日韩国产综合新一区| 日韩亚洲一区在线播放| 亚洲第一精品影视| 欧美人成在线视频| 亚洲永久在线观看| 亚洲专区在线| 国产一区二区av| 久久综合激情| 欧美成va人片在线观看| 一本久道久久综合狠狠爱| 亚洲第一中文字幕| 欧美屁股在线| 欧美成人一区二区在线| 亚洲区一区二区三区| 亚洲精品九九| 国产精品私人影院| 久久久亚洲人| 欧美承认网站| 亚洲欧美在线播放| 久久久精品五月天| 99国产一区| 先锋影院在线亚洲| 91久久黄色| 亚洲视频 欧洲视频| 国产日韩精品一区二区三区在线| 巨乳诱惑日韩免费av| 欧美国产亚洲视频| 欧美一区激情| 欧美精品二区| 久久久噜噜噜久久| 欧美日韩国产在线看| 久久精品视频亚洲| 欧美精品久久久久久久久老牛影院 | 亚洲国产精品999| 妖精成人www高清在线观看| 国产亚洲欧美日韩日本| 亚洲二区免费| 国内自拍一区| 亚洲天堂偷拍| 亚洲精品国精品久久99热| 亚洲新中文字幕| 亚洲精品国产品国语在线app| 亚洲男女自偷自拍| 一区二区欧美国产| 两个人的视频www国产精品| 午夜精品理论片| 欧美区亚洲区| 欧美高清视频一区二区三区在线观看| 国产精品高潮呻吟久久av黑人| 男女精品视频| 国产视频久久网| 亚洲视频电影图片偷拍一区| 国产一区二区在线观看免费播放| 亚洲一区二区三区777| 久久精品理论片| 欧美噜噜久久久xxx| 久久一区中文字幕| 欧美粗暴jizz性欧美20| 国产精品免费看片| 亚洲免费不卡| 亚洲美女啪啪| 欧美不卡一卡二卡免费版| 久久免费精品日本久久中文字幕| 欧美日韩在线另类| 亚洲经典在线| 亚洲人线精品午夜| 欧美大尺度在线观看| 欧美成年网站| 1000部精品久久久久久久久| 欧美一区二区三区四区在线观看地址 | 欧美在线免费| 久久精品国产999大香线蕉| 国产精品海角社区在线观看| 亚洲精品国产系列| 一二三区精品福利视频| 欧美精品精品一区| 亚洲人成网站色ww在线| 日韩系列欧美系列| 欧美精品精品一区| 日韩一本二本av| 亚洲女爱视频在线| 国产精品乱码妇女bbbb| 亚洲欧美bt| 久久免费午夜影院| 在线观看中文字幕不卡| 久久久在线视频| 亚洲电影欧美电影有声小说| 亚洲日韩第九十九页| 欧美精品在线观看91| 亚洲国产一区二区三区在线播 | 久久资源av| 亚洲福利电影| 欧美日本一道本| 亚洲影院高清在线| 久久久www成人免费精品| 国产日韩精品一区二区三区 | 欧美日韩中文在线观看| 99re热这里只有精品视频| 亚洲影视中文字幕| 一区二区三区国产在线| 亚洲一区二区三区三| 国产欧美一区二区视频| 久久精品国产亚洲精品| 亚洲高清资源| 亚洲免费一在线| 国语精品一区| 欧美日韩国语| 午夜在线观看欧美| 亚洲高清av在线| 午夜亚洲一区| 亚洲电影免费在线观看| 欧美日产在线观看| 欧美一区视频| 亚洲欧洲综合| 狠狠干狠狠久久| 亚洲精品国产系列| 久久成人国产| 99综合视频| 国产亚洲亚洲| 欧美日韩免费观看一区三区| 亚洲欧美日韩精品久久久| 欧美激情中文字幕在线| 亚洲欧美一区二区视频| 在线欧美日韩精品| 性色av一区二区三区在线观看| 国产欧美精品在线| 红杏aⅴ成人免费视频| 久久在线免费观看| 99在线观看免费视频精品观看| 国产精品日韩欧美综合| 久久亚洲午夜电影| 亚洲欧美日韩国产一区二区| 亚洲国产精品一区二区久| 欧美在线免费观看| 一本久道久久综合婷婷鲸鱼| 国产亚洲一级| 欧美日韩一区三区| 久久久夜色精品亚洲| 亚洲午夜精品国产| 亚洲东热激情| 免费在线播放第一区高清av| 欧美一级黄色录像| 亚洲图片欧洲图片av| 亚洲毛片在线观看.| 亚洲国产成人精品久久久国产成人一区| 国产精品一区二区久久| 欧美乱大交xxxxx| 免费不卡亚洲欧美| 久久国产精彩视频| 性欧美xxxx视频在线观看| 中国av一区| 亚洲视频一区在线| 亚洲最新视频在线| 亚洲最新色图| 亚洲小视频在线观看| 亚洲五月婷婷| 亚洲欧美日韩精品久久久久| 亚洲天堂男人| 亚洲一线二线三线久久久| 日韩视频在线一区二区| 亚洲欧洲综合另类在线| 亚洲黄一区二区三区| 欧美国产国产综合| 亚洲风情亚aⅴ在线发布| 欧美国产欧美亚洲国产日韩mv天天看完整 | 亚洲国产成人精品视频| 女主播福利一区| 欧美大片免费观看| 欧美国产综合视频| 亚洲国产精品久久91精品| 亚洲国产日韩在线| 日韩小视频在线观看专区| 欧美激情免费观看| 亚洲精品欧洲精品| 国产精品99久久久久久有的能看| 亚洲视频在线观看网站| 羞羞色国产精品| 久久亚洲国产精品日日av夜夜| 蜜桃av久久久亚洲精品| 欧美乱妇高清无乱码| 国产精品乱码一区二区三区| 国内精品国语自产拍在线观看| 国内精品福利| 欧美激情第4页| 一区二区三区视频在线观看| 国产精品成人观看视频免费 |