標籤:
為啥需要枚舉類型
程式設計語言中的所有特性都是為了滿足某種需求,達到某個目的還出現.不會莫名其妙的出現在那.
枚舉可以用來儲存一組屬性的值.enum的全稱是enumeration意思是列舉
看著這句話可能覺得太書面化了,不夠通俗易懂.那舉些通俗的例子說說.日常生活中我們特喜歡分類,比如讀書時分啥數,理,化.當官的層級有啥省長,市長,縣長.軍隊有軍長,師長,團長.這樣一組組的屬性值就最適合用枚舉類型來表示.當用一個軟體時,有些頁面會有很多選項按鈕(radio button),這也特別適合用枚舉來表示你舉了哪一個.
光這樣說你可能還不能體現枚舉的好處.如果沒枚舉,表示一些組屬性的值你只能用一組數字,或者一組字串.
數字從字面上看不出任意意義,可讀性非常差,所以很少用.那就假如要你通過比較字串來做很多種類判斷,比如 if(HisTitle == "stadholder") else if(HistTile == "mayor") .如果讓你敲個幾十次你就知道是件多麻煩痛苦的事了,很多單詞如果敲錯一個意思就完全變了,這樣出現bug了也不容易找到.老是複製粘貼也較麻煩.所以字串是編輯麻煩,容易出錯.如果要使用枚舉就極大的方便我們敲代碼,整合開發工具中的智能感應會給你提示,敲個點號就帶出來了.而且枚舉會做類型檢查,不會像字串那樣只能靠你自己去對比.
枚舉類型的記憶體配置問題
上面我們講了如果沒有枚舉,一般會想到用數字或字串表示某個類別.這樣使用肯定不方便.也許你可能會想到用宏來表示.比如#define 市長 "mayor" 或者#define 市長 2
這自然是一個方法,但一來嘛在C++中是不太推薦用宏的,盡量少用.因為C++是強型別的語言,希望通過類型檢查來降低程式中的很多錯誤,而宏只是在編譯期前做簡單替換,繞過了類型檢查,失去了強型別系統的優勢支撐. 二來嘛一組屬性值都是相關聯的資訊,必須放到一起,放到一組.
關於常量的誤解
枚舉類型成員是常量
這句話怎麼理解呢.也就是說enum MyEnum{ one = 1 , two , three} ;
與 const int one = 1; const int two = 2; const int three = 3; 差不多是一樣的.
說到常量其實有個非常誤導人的地方因為用宏#define 可以定義的我們說是常量,這裡只涉及到簡單的替換自然不可能存在記憶體配置問題.但是用const定義的也叫常量,而const定義常量貌似跟定義一般的變數只多個const關鍵字. 你可能會想當然認為常量都只是簡單替換,所以不存在記憶體配置.那按這個邏輯,豈不是const定義的常量,枚舉類型都沒有記憶體配置?
實際上大部分時候確實是這樣的.但並非總是如此,有些情況會需要分配記憶體的.
1.不需要分配記憶體的情況
如果定義常量const int one = 1;然後在其他地方只是把one作為右值賦值給其他變數那就不存在記憶體配置.但這裡的常量跟#define定義的常量不同,宏定義的常量是編譯前簡單替換掉,而不需要做類型檢查.而const定義的常量在編譯時間會幫類型檢查,編譯完之後再做替換.所以編譯完之後就看不到const的資訊了,轉換成對應的值.const定義的資訊只是儲存在符號表中.
那同樣,如果只是enum MyEnum{ one = 1 , two , three} ;這樣定義一個枚舉類型,然後也是簡單的作右值賦值給其他變數.比如int num = MyEnum::one;那也只是儲存資訊在符號表中,編譯後被替換掉了.
有人可能說如果用sizeof MyEnum測下會發現會是4(這是VS裡面,不同的編譯器可能不一樣)於是認為不管是枚舉裡面有多少個元素記憶體配置都是4.實際上不是這意思,應該是定義一個MyEnum類型的枚舉變數時會分配記憶體.這跟定義了一個類一樣,你用sizeof去測一個為也會看到大小,但我們知道只有當類執行個體化之後才實際分配記憶體的.
2.需要分配記憶體的情況
1.)const int one;是類的成員變數 2. )extern const int one = 123; 3.)const int one = 1; int* pConst = &one;
上面三種情況會需要分配記憶體.
而枚舉類型,如果不是簡單的去給其他變數賦值,而是去定義一個枚舉類型變數.
比如MyEnum grade = MyEnum::one; //此時會分配4位元組記憶體空間.(不過據說編譯器會做最佳化,如果枚舉類型所有值用兩個位元組表示就足夠了,那實際分配的會就只會是兩位元組了.不一定就是預設的int類型的長度)
枚舉類型具體用法
一般的用法是在全域域內定義一個枚舉類型.比如
enum MyEnum { one, two, three }
如果不顯式指定,就會把第一個值預設賦值為0,然後遞增1依次賦值.如果顯式指定了某個值,則它下一個是它加1.
所以上面的例子中預設one = 0; two = 1; three= 2;
如果顯式指定enum MyEnum { one, two = 3, three }
則one = 0; two = 3; three = 4;
定義一個枚舉類型就是MyEnum grade = MyEnum::one;
類中使用枚舉這是不太常用的用法.
在類中聲明一個枚舉後,定義枚舉類型就可以省掉那個域作用首碼.比如MyEnum my = one; 在相同的範圍內也不能出現某個變數的名字和枚舉中的元素名字相同,也就是不能出現其他變數名字是on,two, three
另外枚舉還有一種少見的用法是
enum { one ,two ,three}; 就是不指定一個名字,這樣我們自然也沒法去定義一些枚舉類型了.此時就相當於const int one = 0;這樣定義三個常量一樣.
然後用的話就是int no = one;
初始化時可以賦負數, 以後的標識符仍依次加1;
x = 2;
是不允許是,如果對X進行賦值,只能對3進行類型轉換.即:
x = (string)2;
那麼這樣就對了.
如果給x賦的不是一個整形的數,而是一個字元型的,如:
x = (string)’a’;
那麼這時候x的值並不是字元’a’,而是’a’的ASCII碼,我們知道,在枚舉類型中,各常量的值只能是整形的,所以在對上例會自動的將’a’轉換成一個整數值.從記憶體的角度來看來話,其實C/C++中整形和字元型的變數是一樣的,它們之間可以互相轉換.
比如定義
namespace A
{
enum B { b1, b2};
class C
{
};
}
其中我們認為B是一種類型,在類型這個方面我們可以理解enmu跟class,struct是平等的,所以我們可以放心的定義
A::B = A::b1;
注意右邊的賦值,本質上b1是直接定義在命名空間A之內的,他相當於A的一個public的const量。
有時候在命名空間中直接定義了太多的enmu是不合理的,這樣會汙染命名空間,所以,儘可能的,我們要在類的內部定義enmu變數。
同樣,關於enum的預設數值的問題需要主義。enmu總是從0開始的,如果我們不指定預設值,則兩個在同樣的命名空間的enum將會都從0開始,大部分時候這並不會出現問題,但是為了防患於未然,我們盡量要
1. 將enum定義在合適的命名空間中
2. 為enum指定預設值
C++ enum