日々の記録。

プログラミングのメモや感じた事などを記録。

C++講座 その6 列挙型

列挙型を利用すると、定数の集合に名前を付ける事ができ、新しい型のように扱う事ができる。

enum Sex {
MALE,
WOMAN
};

enum ButtonType {
NORMAL,
IMAGE,
TOGGLE
};

定数は明示しなければ0から連番で0,1,2,...と付けられる。 = 1のように値を明示することもできる。

enum ResultType {
NO_ERROR = 0,
IO_ERROR = -1,
MEMORY_ERROR = -2
};

サイズ

enum内の定数値にint, unsigned intとして表現できない値が含まれていない限り、sizeof(int)よりは大きくならない。

サイズは環境による(コンパイラオプション等で変更できるかもしれない)。

メリット

enumを利用する事の最大のメリットは、関数などの引数で渡す値や戻り値を明示できる点にある。

例えば

#define NO_ERROR (0)
#define IO_ERROR (-1)

int foo() {
  return NO_ERROR;
}

void bar() {
  int result = foo();
}

と書くよりも

enum Result {
IO_ERROR = -1
NO_ERROR,
};

Result foo() {
  return NO_ERROR;
}

void bar() {
  Result result = foo();
}

と書いた方が、関数の戻り値が明確になり可読性が高まる。コンパイラも型をチェックしてくれるので、誤った定数値を渡すリスクも減らせる。

デメリット

ないと思う。 以前、enum禁止のコーディングルールを見たことがあるけど信じられない。

注意点

C言語ではtypedefを付ける

C++ではtypedef不要だけど、Cでは必要(C99未確認です)。 定義ファイルをCでも共有する場合は、typedefを付ける必要がある。

通信プロトコルでの利用時

TCP/IPを利用する場合、recv/sendで構造体を直接読み書きする場合には、環境によりサイズが異なる可能性があるため、enumを利用しないこと(ちゃんと構造体毎にシリアライズする関数を定義してあるなら別かもしれませんが・・・。)。

enumの定数名はグローバルに属するため重複しやすい

namespace、classに属さないenumの定数名は、グローバルに属するため一意にする必要がある。 C++で定義する場合、classまたはnamespace内で定義すること。

enum A {
  AAA,
  BBB,
  CCC
};

enum B {
   CCC,
   DDD,
   EEE
};

void foo() {
  AAA // OK
  CCC // コンパイラはA::CかB::Cか区別できない.
}

namespace aaa {
  enum A {
    AAA,
    BBB,
    CCC
  };
}

namespace bbb {
  enum B {
     CCC,
     DDD,
     EEE
  };
}

void foo() {
  aaa::AAA // OK
  aaa::CCC // OK.
}

おまけ

定数の数を定義

enum DEVICE_TYPE {
  IPHONE_TYPE,
  IPAD_TYPE,
};

#define NUM_DEVICE_TYPE (2)

としても良いけど、定数値が0から連番で始まるなら

enum DEVICE_TYPE {
  IPHONE_TYPE,
  IPAD_TYPE,
  NUM_DEVICE_TYPE
};

これでも良い、と思う。