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

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失?。徊坏扔贓RROR_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>
            99re这里只有精品6| 国产精品欧美激情| 巨胸喷奶水www久久久免费动漫| 这里只有精品电影| 一本色道久久综合亚洲精品不卡 | 欧美日本亚洲| 欧美日韩国产探花| 国产精品国产三级国产专播品爱网 | 久久久久久久欧美精品| 免费看成人av| 国产精品福利网站| 在线成人国产| 亚洲一区免费| 久久婷婷蜜乳一本欲蜜臀| 亚洲国产欧美一区二区三区久久| 欧美韩国一区| 亚洲一区www| 开心色5月久久精品| 欧美日韩精品免费在线观看视频 | 亚洲国产三级在线| 中文久久精品| 久久综合久久综合久久| 亚洲精品国产拍免费91在线| 性欧美1819性猛交| 欧美日韩高清不卡| 1769国产精品| 久久精品国产69国产精品亚洲| 欧美激情一级片一区二区| 性色av一区二区三区| 欧美日韩在线影院| 亚洲激情在线| 久久中文字幕导航| 亚洲欧美福利一区二区| 欧美美女喷水视频| 亚洲国产一区在线| 久久天天狠狠| 午夜精品一区二区在线观看| 欧美精品综合| 亚洲精美视频| 久热国产精品| 欧美一区1区三区3区公司| 欧美日韩精品一区二区天天拍小说| 一区二区三区在线免费播放| 欧美中文字幕在线观看| 亚洲手机成人高清视频| 欧美色另类天堂2015| 亚洲开发第一视频在线播放| 免费国产自线拍一欧美视频| 欧美一区激情| 国产有码一区二区| 久久久综合激的五月天| 香蕉乱码成人久久天堂爱免费| 国产精品网站在线观看| 亚洲男人第一网站| 韩国女主播一区二区三区| 国产亚洲欧美一区二区三区| 亚洲视频狠狠| 一本色道婷婷久久欧美| 欧美理论电影在线播放| 亚洲美女在线看| 亚洲精品无人区| 欧美日韩国产综合视频在线观看| 99在线视频精品| 99精品国产在热久久| 国产精品v欧美精品∨日韩| 亚洲一区区二区| 亚洲欧美大片| 国产综合久久久久久鬼色| 久久综合色天天久久综合图片| 久久久久久91香蕉国产| 亚洲国产导航| 亚洲三级影院| 国产精品av久久久久久麻豆网| 亚洲在线观看| 欧美在现视频| 亚洲欧洲综合| 一区二区三区视频在线播放| 国产精品午夜国产小视频| 久久蜜臀精品av| 美女主播一区| 亚洲一区二区三区四区中文| 性娇小13――14欧美| 亚洲黄色一区| 亚洲已满18点击进入久久 | 99re66热这里只有精品4| 日韩写真在线| 国产一区二区三区最好精华液| 欧美激情按摩在线| 国产精品久久久久99| 老妇喷水一区二区三区| 欧美日韩国产综合一区二区| 久久激五月天综合精品| 免费中文字幕日韩欧美| 午夜精品久久久久久| 久久久亚洲一区| 亚洲永久免费观看| 欧美11—12娇小xxxx| 欧美一区二区三区在线观看视频| 久热精品视频在线观看一区| 亚洲欧美日本日韩| 欧美成人黄色小视频| 欧美中文在线观看| 欧美日韩第一区| 男女av一区三区二区色多| 国产精品美女久久久久久2018| 欧美国产视频日韩| 国产中文一区二区| 在线一区二区三区四区五区| 亚洲激情网址| 久久免费视频网站| 久久国产主播| 国产精品精品视频| 亚洲人体大胆视频| 亚洲国产欧美久久| 久久精品夜夜夜夜久久| 欧美一区二区三区在线播放| 欧美午夜精品久久久久久人妖| 久久久久久久久久久一区| 欧美三级电影大全| 欧美jjzz| 在线日韩av永久免费观看| 亚洲一级高清| 亚洲性色视频| 欧美三级在线播放| 亚洲精品中文字幕有码专区| 在线观看欧美亚洲| 久久九九久久九九| 老司机免费视频一区二区| 国产一区二区视频在线观看| 亚洲欧美日韩在线播放| 亚洲欧洲99久久| 国产精品久久综合| 亚洲欧美激情视频在线观看一区二区三区 | 国产伊人精品| 久久国产综合精品| 久久蜜桃香蕉精品一区二区三区| 国产一区久久| 久久三级福利| 亚洲第一天堂av| 日韩天堂在线观看| 欧美日韩一区二区三区在线看 | 亚洲精品视频在线看| 日韩小视频在线观看专区| 欧美看片网站| 亚洲网友自拍| 久久久久国色av免费观看性色| 国产亚洲精品bt天堂精选| 久久国产99| 欧美高清视频一二三区| 日韩一区二区精品视频| 欧美日韩在线播放三区| 亚洲一区二区三区在线观看视频| 亚洲欧美日韩一区| 国产自产2019最新不卡| 免费看的黄色欧美网站| 99精品久久免费看蜜臀剧情介绍| 亚洲男人影院| 一区二区三区在线看| 欧美激情一区二区三区不卡| 在线亚洲一区二区| 免费成人黄色av| 亚洲视频欧美视频| 国产一区二区三区四区在线观看| 久久人人精品| 一本一本大道香蕉久在线精品| 久久久久久久久久看片| 亚洲破处大片| 国产精品一区在线播放| 久久久精品日韩欧美| 一二三区精品福利视频| 久久综合999| 亚洲综合二区| 亚洲国产一区在线| 国产视频综合在线| 欧美韩日高清| 欧美影院午夜播放| 一本久道久久久| 欧美bbbxxxxx| 欧美在线亚洲综合一区| 亚洲乱码国产乱码精品精98午夜| 午夜精品影院在线观看| 国产亚洲一区二区精品| 欧美精品一区二区三区在线播放| 西瓜成人精品人成网站| 亚洲激情一区二区三区| 欧美一区成人| 夜夜嗨av一区二区三区四区| 国外成人性视频| 欧美日本不卡视频| 久久―日本道色综合久久| 午夜精品视频在线观看一区二区| 亚洲伦伦在线| 亚洲欧洲免费视频| 亚洲高清精品中出| 欧美成人精品在线| 美女日韩欧美| 久久青青草原一区二区| 香蕉久久夜色精品国产| 亚洲网站在线看| 亚洲手机视频|