C++マニアック

enum の使い方,列挙型の使い方,how to use enum,C++

このページでは C++ の enum(列挙型) の使い方について解説し、enum の文字列化クラス作成ツールを公開します。

  1. enum(列挙型)とは
  2. 文字列化クラス(構造体)の自動生成
  3. enum(列挙型)と switch 文
  4. C++ 関連本

enum(列挙型)とは ページの先頭へ

列挙型とは、ざっくり言ってしまえば、「選択肢」を表す整数の定数を定義するための変数型です。

列挙型を使わなくても整数の定数は様々な方法で定義できます。例えば、「左と右」という二つの状態を表すために、#define を使って、次のような定義を行うことが出来ます。

#define LEFT  0
#define RIGHT 1

別の方法として、const int 型の変数を定義するということも可能です。

const int iLeft = 0;
const int iRight= 1;

しかし上記は、「選択肢」を表すという意味では、少し問題があります。0 が左、1 が右までは良いのですが、整数は別の値にもなり得るため、では、2 とか 3 になったらこれは一体どういう意味になるのか? という問題が発生します。まぁ、それらには意味はないということなのですが、ただの整数では「選択肢」を表すために値を限定するという機能がないわけです。つまり、次のように書けてしまいます。

int iLeftOrRight = 3;     // 右でも左でもない値が、代入できてしまう。

上記のような問題を避けるために列挙型は存在します。enum を使って、上記と同様の事情を次のように定義できます。

enum EDirection { E_Left = 0, E_Right = 1, };

これで、次のように、選択肢以外の値は入れられない EDirection 型(列挙型)の変数が定義できるようになります。

#include <stdio.h>

enum EDirection { E_Left = 0, E_Right = 1, };    // 列挙型の定義

int main() {
    EDirection eleftorright;         // 列挙型変数 eleftorright を定義する。値は、未定義である。
    eleftorright = E_Left;              // 列挙型定数の代入は当然 OK である。
//  eleftorright = 0;                   // @const int を EDirection に変換できないというエラーになる。
    eleftorright = EDirection(100);  // A整数を明示的にキャストすれば、代入できるが、ほぼ無意味である。
    printf("%d\n", eleftorright);       // printf では、enum は、整数として扱われる。100 と表示される。
    return 0;
}

上記の@の行のコメントを外すと、コンパイル時に const int を EDirection に変換できないというエラーになります。このように、でたらめな値が代入されないということに意味があるわけです。

しかし、Aのように、無意味な値でも EDirection 型にキャストすると、代入できてしまいます。意図的に、でたらめな値を代入することは、許されているというわけです。

まとめると、列挙型の変数には、意図せずにでたらめな値が書き込まれることがないようになっているが、意図すればそれも可能である、ということで、「選択肢」を表現するために単なる整数を使用するよりは、ずっと安全確実なコードが書けるが、意図すればそうでないようにもできる、ということになります。列挙型とは、そういうものなわけです。

ちなみに、列挙型の定義の整数値は、通常は明示的に書く必要がなく、その場合には、定義順に 0, 1, 2, ... が割り当てられます。

enum EDirection { E_Left = 0, E_Right = 1, };    // の部分は、なくても良い。

また、値を明示的に書くときには、同じ整数値を複数の列挙子(上の例では E_Left など)に意図的に割り当てることも可能になっています。それを何に使うかは、あなた次第なわけですが。

列挙型をクラスの内部で定義することも可能で、その場合には、クラスの内部では列挙型、列挙子をそのまま使用できます。クラスの外部からは、クラス名:: で修飾すればアクセス可能です。次の例を見てください。

class CManiac {
public:    // クラスの外で使うなら、public または protected とする。
    enum E_HowManiac { E_ALittle, E_Very, E_Ultra, E_Super, }; // クラスの中で列挙型を定義する。
public:
    void Func() {
        E_HowManiac howmaniac = E_Super;      // クラスの中では、そのまま使用できる。
    }
};

int main() {
    CManiac::E_HowManiac howmaniac = CManiac::E_Ultra; // クラスの外では、CManiac:: で修飾する必要がある。
    return 0;
}

文字列化クラス(構造体)の自動生成 ページの先頭へ

さて上記のように、列挙型に何も難しいところはないのですが、少し、不便なところはあります。それは、C++ では、enum(列挙型) の値を例えば printf で表示しようとすると、整数として表示するしか方法がないということです。

例えば、もうお馴染みの次のような enum の定義があったとして、

    enum EDirection {
        E_Left   = 0,       // コメント
        E_Right  = 1,
    };

列挙値を printf しようとすると、次のように書くことになり、

	printf("Direction = %d\n", E_Left);

その出力は、当然整数表示になります。

Direction = 0

これでは、必ず、0 って、何だ? ということになります。とてもわかりにくく不便なわけですね。だから、C# のほうがいいじゃないか、なんて非本質的な話にもなりかねません。

そこで enum(列挙型)の値を、文字列で表示するためにツールを作成してみました。下の窓に enum の定義を貼り付けて、「enum 文字列化クラス作成」ボタンを押すと、元の enum の定義に随伴すべき文字列化クラスを自動生成します

例えば、上に出てきた enum の定義から自動生成されたクラス(構造体)は、次のようになります。enum を定義するごとに、これを手で書くのでは、疲れますが、ツールがあれば、コピペするだけで完成です。

    struct StrEDirection : public string {
        StrEDirection(EDirection e) {
            switch(e) {
            break; case E_Left : { assign("E_Left" ); }
            break; case E_Right: { assign("E_Right"); }
            break; default: { assign("illegal"); }
            }
        }
    };

ちょっとやってみてください。

当サイトでは、入力された文字列の保存は一切行っていません。しかし、平文がネットワークを通りますので、そういう意味で差し障りの無い文字列を入力してください。また、出力されたコードによって生じる利益、損害などあらゆるものについて、何も保証はいたしませんので予めそのような条件を納得された上でご使用ください。念のため。


押すと結果が別窓で表示されます

出来上がった上記クラスの使い方は、ほぼ、次のようなものになるでしょう。

    printf("Direction = %s", StrEDirection(E_Left).c_str());

上記の出力結果は、次のように文字列表示になり、0 よりはずっと意味が分かりやすいわけですね。

Direction = E_Left;

enum(列挙型)と switch 文 ページの先頭へ

上の文字列化クラス(構造体)の中でも switch 文を使用しましたが、列挙型は switch 文と非常に相性が良いという特徴があります。switch 文は、数ある選択肢の中から一つを選んだら何が起きるのかを記述するという構造になっているため、選択肢を表現する enum(列挙型) と相性が良いのは、当然といえば当然です。

列挙型変数に対してできることは、値を代入することと、同じ列挙型の変数または定数と値を比較することだけなので、使い方としては、switch 文あるいは if 文で選択肢ごとの動作を記述する事に尽きると思って、あまり間違いはないでしょう。

switch 文を使ったコードは、概念的には、常に、ほぼ次のような形になるでしょう。

switch(列挙型変数) {
break; case 選択肢0: { 動作0; }
break; case 選択肢1: { 動作1; }
    :
break; case 選択肢n: { 動作n; }
break; default: { 列挙型変数が、どの選択肢にも合致しない場合の動作; }
}

同じことは if 文を使用しても記述できて、常に、ほぼ次のような形になるでしょう。

if (false) {}
else if (列挙型変数 == 選択肢0) { 動作0; }
else if (列挙型変数 == 選択肢1) { 動作2; }
    :
else if (列挙型変数 == 選択肢n) { 動作n; }
else { 列挙型変数が、どの選択肢にも合致しない場合の動作; }

どちらを使うかは、ケースバイケースですが、殆ど、好みの問題でしょう。