C++列挙

6264 ワード

C++列挙タイプenumを深く理解する
なぜ列挙タイプが必要なのか
プログラミング言語のすべての特性はある需要を満たすために、ある目的を達成するために現れた.わけのわからないことはそこに現れない.
列挙は、属性のセットを保存するために使用できる値である.Enumのフルネームはenumerationの意味は列挙です
この言葉を見ていると、あまりにも書面化されていて、分かりにくいと思うかもしれません.それは通俗的な例を挙げて話します.日常生活の中で私达は特に分类が好きで、例えば本を読む时何数、理、化.官のレベルは何の省長があって、市長、県長.軍隊には軍長、師長、団長がいる.このようなグループの属性値のセットは、列挙型で表すのが最適である.ソフトウェアを使用する場合、いくつかのページにはラジオボタン(radio button)がたくさんあります.これは、どのページを挙げたかを列挙するのに特に適しています.
 
このように言うだけでは、列挙のメリットを体現できないかもしれません.列挙されていない場合、いくつかのグループの属性を表す値は、数値のセット、または文字列のセットしか使用できません.
数字は字面から任意の意味が見えず、可読性が非常に悪いため、あまり使われない.if(HisTitle=="stadholder")else if(HistTile="mayor")など、文字列を比較することで多くの種類の判断を行う必要があります.何十回も叩くと、どんなに面倒でつらいことかがわかります.多くの単語が1つの意味を間違えると完全に変わります.バグが発生しても見つけにくいです.コピー&ペーストばかりするのも面倒です.だから文字列は編集が面倒で、間違いやすい.列挙を使うと便利です.コードを叩くのに便利です.集積開発ツールのスマートセンサーがヒントを与えます.ポイント番号を叩くと出てきます.そして列挙はタイプチェックをして、文字列のように自分で比較するしかありません.
 
列挙タイプのメモリ割り当ての問題
 
列挙がなければ、あるカテゴリを数字や文字列で表すことが一般的である.このように使うのは不便に違いない.マクロで表現したいかもしれません.例えば#define市長「mayor」とか#define市長2
これはもちろん一つの方法ですが、C++ではマクロの使用はあまりお勧めできませんので、できるだけ少なくしてください.C++は強いタイプの言語であるため、タイプ検査によってプログラム中の多くの誤りを低減することを望んでいるが、マクロはコンパイル期間前に簡単な置換を行い、タイプ検査を迂回し、強いタイプのシステムの優位性の支持を失った.2つ目は、属性値のセットが関連する情報であり、一緒に配置し、グループに配置しなければならない.
 
定数についての誤解
列挙タイプメンバーは定数です
この言叶はどう理解しますか.つまり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}だけなら;このように1つの列挙タイプを定義し、その後も簡単に右値を他の変数に与える.例えば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;
上記3つの場合にはメモリの割り当てが必要になる.
一方、列挙タイプは、単純に他の変数に値を付与のではなく、列挙タイプ変数を定義する.
例えばMyEnum grade=MyEnum::one;//4バイトのメモリ領域が割り当てる.(ただし、コンパイラは最適化するそうで、列挙タイプのすべての値を2バイトで表すだけで十分であれば、実際に割り当てられるのは2バイトだけです.必ずしもデフォルトのintタイプの長さではありません)
 
 
列挙タイプの具体的な使い方
一般的な用法は、全局所内で列挙タイプを定義することである.たとえば
enum  MyEnum { one, two, three }
明示的に指定しない場合は、最初の値のデフォルト値を0にし、1を増やして順次値を割り当てる.明示的に値を指定した場合、次は1を加算します.
したがって、上記の例ではone=0がデフォルトです.two = 1; three= 2;
enum MyEnum{one,two=3,three}を明示的に指定すると
one=0two = 3; three = 4;
 
列挙タイプを定義するとMyEnum grade=MyEnum::one;
 
クラスに列挙を用いるこれはあまりよくない用法である.
クラスに列挙を宣言すると、列挙タイプを定義することで、そのドメインの役割の接頭辞を省くことができる.例えばMyEnum my=one;同じ役割ドメイン内にもある変数の名前と列挙中の要素の名前が同じではありません.つまり、他の変数の名前はon,two,threeではありません.
また、列挙には珍しい使い方があります.
enum { one ,two ,three};名前を指定しないと、列挙タイプを定義することはできません.この場合const int one=0に相当します.このように定義3つの定数は同じである.
そして使うとint no=one;
C++11強タイプ列挙
【C++11強タイプ列挙】
標準C++では、列挙タイプはタイプセキュリティではありません.列挙タイプは整数と見なされ、2つの異なる列挙タイプ間で比較できるようにします.C++03が唯一提供するセキュリティメカニズムは、1つの整数または1つの列挙値が暗黙的に別の列挙型に変換できないことである.また、列挙に使用される整数タイプとそのサイズは、実装方法によって定義され、明確に指定できません.最後に,列挙名の全数が一般に露出しているため,C++03の2つの異なる列挙は,同じ列挙名を持つことはできない.(例えばenum Side{Right,Left};enum Thing{Wrong,Right};と一緒に使用できません.)
C++11は,上記の問題を回避できる特別な「列挙クラス」を導入した.enum classの構文を使用して宣言します.
enum class Enumeration
{
  Val1,
  Val2,
  Val3 = 100,
  Val4 /* = 101 */,
};

この列挙はタイプが安全である.列挙タイプは暗黙的に整数に変換できない.整数値と比較することもできません.(式Enumeration::Val4 == 101はコンパイル期間エラーをトリガーすることを示す).
列挙タイプで使用されるタイプは、明示的に指定する必要があります.上記の例では、デフォルトタイプintを使用していますが、他のタイプを指定することもできます.
enum class Enum2 : unsigned int {Val1, Val2};

列挙タイプの語彙範囲(scoping)は、列挙タイプの名前範囲に定義されます.列挙タイプの列挙名を使用する場合は、その属する範囲を明確に指定する必要があります.前述の列挙タイプEnum 2を例にとると、Enum 2::Val 1は有意義な表現であり、単独のVal 1はNOである.
さらに、C++11では、従来の列挙の使用タイプを指定できます.
enum Enum3 : unsigned long {Val1 = 1, Val2};

列挙名Val 1は、Enum 3の列挙範囲(Enum 3::Val 1)に定義されていますが、互換性のため、Val 1は一般的な範囲で単独で使用できます.
C++11では、指定可能なタイプの新しい列挙を使用すれば、列挙タイプの事前宣言(forward declaration)も可能です.以前のC++が列挙の前置き宣言を書くことができなかったのは、列挙パラメータが占める空間の大きさを決定できなかったためであり、C++11はこの問題を解決した.
enum Enum1;                     //      C++   C++11;       
enum Enum2 : unsigned int;      //     C++11
enum class Enum3;               //     C++11,           int 
enum class Enum4: unsigned int; //     C++11
enum Enum2 : unsigned short;    //      C++11,Enum2       unsigned int