第76章 列挙子

 第74章でやった bool 型は、true と false の2種類の値しかとることができませんでした。こういった数種類の値しかとることのできない型というのが作れると、いろいろ便利なことがあります。今回はそういった話です。


 それでは今回の要点です。


 では、いってみましょう。


 第58章で構造体について話しました。構造体とは、ある変数をまとめて宣言できるような型のことでした。

 今回も「型を作る」ということをやります。今回の型はちょっと変わっていて、「ある一定の値しかとることのできない型」というものです。名前は列挙型です。

 では、早速次のプログラムを見て下さい。

プログラム実行結果例
// Enum1.cpp
#include <iostream.h>

enum ECompare
{
    LESSTHAN    = 0,
    EQUALTO     = 1,
    GREATERTHAN = 2,
};

ECompare Compare(int a, int b)
{
    return (a < b) ? LESSTHAN :
           (a > b) ? GREATERTHAN : EQUALTO;
}

bool Compare()
{
    int a, b;

    cout << "数字を2つ入力して下さい > " << flush;
    cin >> a >> b;

    if(a == -1)
        return false;

    switch(Compare(a, b))
    {
    case LESSTHAN:
        cout << "前者は後者より小さいです。" << endl;
        break;

    case EQUALTO:
        cout << "両者は等しいです。" << endl;
        break;

    case GREATERTHAN:
        cout << "前者は後者より大きいです。" << endl;
        break;
    }

    return true;
}

int main()
{
    while(Compare());

    return 0;
}
数字を2つ入力して下さい > 0 1
前者は後者より小さいです。
数字を2つ入力して下さい > 1 1
両者は等しいです。
数字を2つ入力して下さい > 1 0
前者は後者より大きいです。
数字を2つ入力して下さい > -1 0

 2つ値を入力させ、大きいか、小さいか、等しいかを表示するプログラムです。なんというか、まぁ、意味のないプログラムで、実行結果例もいいかげんですが、勘弁して下さい。

 列挙型の宣言は

enum ECompare
{
    LESSTHAN    = 0,
    EQUALTO     = 1,
    GREATERTHAN = 2,
};

の部分です。こうすると、ECompare 型の変数は LESSTHAN, EQUALTO, GREATERTHAN の3つの値しかとれない変数になります。値はこのように列挙子と呼ばれるテキストで表します。列挙子の値は = で代入するようにすれば設定できます。

 構造体の時と同じように、これは ECompare という型を作っただけで、まだ ECompare 型の変数というのはどこにもありません。そして今回は変数は作らず、関数の戻り値にしました。

C言語:enum ECompare Compare(int a, int b);
C++:ECompare Compare(int a, int b);

 これも構造体の時と同じく、C言語の時は enum ECompare と書かなければならず、C++の時はこのように ECompare だけで構いません。C++でも enum を書いても構いません。typedef を使えばC言語でも無精することができます(第60章参照)。

 Compare 関数は、a と b とを比較して結果を LESSTHAN, EQUALTO, GREATERTHAN の3値で返す関数です。もしこの時別の値を return しようとしてもエラーが出ます(return 3; でも、実際にやってみて下さい)。このように、列挙型を使っていれば変な値を返そうとするとエラーが出るので、多少のミスは防ぐことができます。

 で、結果を switch 文で評価しています。ここでも LESSTHAN などと比較しています。が、実は switch 文を通すと列挙型の値はただの int 値と見なされ、それを LESSTHAN などの列挙子の値と比べているだけです。ここで case 3: や default: などがあっても問題はありません。もちろん、これは switch 文のみの問題であって、if 文ではこのようなことはありません。

 そして、このことから分かるとおり、列挙型の値は int 型、unsigned int 型の変数に代入することができます。また、列挙子の値は int 値であり、列挙型のサイズは int 型と同じになります。

 このように、列挙型を使えば特定の値のみを扱うことができるようになるのです。


 この列挙型 ECompare は列挙子が3つと少なかったのですが、これが10個20個となると値の設定が面倒です。そのため、連続する値で設定するときは省略できるようになっています。さらに、一番初めの値を省略すると0になります

 このことを踏まえると、例えば上の ECompare は単純に

ECompare
{
    LESSTHAN, EQUALTO, GREATERTHAN,
};

だけですむことが分かります。便利ですね。

 またこのようにすればいちいち列挙子の値を考えずにすみ、値をもっと抽象的に扱うことができるようになりますね。

 ついでに言えば、同じ値を持つ列挙子は何個あっても構いません。これも値を抽象的に扱うのに一役買っています。


 しかし、この列挙型、致命的な「穴」があります。

 列挙型でキャスト(第21章参照)すると、どんな値でも扱えるようになるのです。これは大きな穴です。何と、

ECompare comp;
comp = (ECompare)3;

はエラーにならないのです。

 実際には ECompare の扱う値以外をこのように入れることはタブーであり、キャストするにしても ECompare の扱う値のみしか代入しないように自分で気を付ける必要があります。これは厳守する必要があります。

 列挙型は、実際には第49章のフラグを宣言するのによく使います。単なる、ある関連のある値の定義にも使います。つまりは、const int や #define の代わりとして使うことが多いのです。

 列挙型の値は上の「穴」のために保証されるものではありませんが、気を付けていさえすれば保証できます。折角列挙型という機能があるのですから、単なる定数代わりなだけではなく、きちんとした型として利用したいものですね。


 それでは、今回の要点です。


 次回も型関係の話題です。文字列の配列を扱いたいときにどうするか、というものです。それでは。


第75章 乱数 | 第77章 リテラル文字列

Last update was done on 2000.8.29

この講座の著作権はロベールが保有しています