【C++】列挙型の理解を深める7つの具体例

C++列挙型を学ぶプログラマーの手引きのイメージC++
この記事は約15分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

プログラミングでは、様々なデータ型が存在し、それらを理解し使用することは非常に重要です。

特にC++において、列挙型(Enum)は、限られた数の定数を表すためのデータ型として広く用いられています。

この記事では、C++における列挙型の基本から、具体的な使用方法、応用例に至るまで、分かりやすく解説していきます。

列挙型を学ぶことで、プログラムの可読性や安全性を高め、より効果的なコーディングが可能になります。

●C++と列挙型の基本概念

C++における列挙型は、プログラム内で固定の定数セットを作成する際に使用されます。

列挙型を使用することで、意味のある名前を数値に関連付けることができ、コードの可読性が向上します。

例えば、曜日や月、状態など、限られた選択肢を持つデータを扱う場合に非常に便利です。

○C++における列挙型の定義

C++で列挙型を定義するには、enum キーワードを使用します。

enum の後に列挙型の名前を付け、その後に波括弧 {} 内に列挙する値を記述します。各値はカンマ , で区切ります。

例えば、曜日を表す列挙型は下記のように定義することができます。

enum Day {
    Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
};

この例では、Day という列挙型が定義され、曜日ごとに固有の整数値が割り当てられます。

C++では、明示的に値を指定しない場合、デフォルトで0から始まる整数値が割り当てられます。

このため、上記の例では Sunday には0、Monday には1というように値が割り当てられます。

○列挙型の基本的な使い方

列挙型を定義した後は、その型を使って変数を宣言し、値を割り当てることができます。

例えば、ある変数が特定の曜日を保持しているとしましょう。

その場合、下記のように列挙型を使用することができます。

Day today = Sunday;

ここで、today という変数が Day 型で宣言され、Sunday という値が割り当てられています。

これにより、プログラム内で today 変数を使って操作を行う際、整数値を直接使用するよりも、より明確で読みやすいコードを書くことができます。

●列挙型の詳細なサンプルコード

C++における列挙型をより深く理解するために、具体的なサンプルコードをいくつか見ていきましょう。

○サンプルコード1:基本的な列挙型の宣言と使用

最初の例として、基本的な列挙型の宣言とその使用方法を紹介します。

下記のコードでは、色を表す列挙型 Color を宣言し、それを使用しています。

// 色を表す列挙型Colorの定義
enum Color {
    Red, Green, Blue
};

// 列挙型Colorを使用してcolor変数を宣言
Color color = Red;

// color変数の値に応じた処理
if (color == Red) {
    // 赤色の場合の処理
}

このコードでは、Color 型を使って color という変数を宣言し、それに Red(赤)を割り当てています。

その後、color の値が Red であるかどうかを判定しています。

このように列挙型を使用することで、色の種類を明確に表現でき、コードの可読性が向上します。

○サンプルコード2:列挙型を使った状態管理

次に、列挙型を使った状態管理の例を見ていきましょう。

下記のコードでは、デバイスの状態を表す列挙型 DeviceState を定義し、その状態に応じて異なる処理を行っています。

// デバイスの状態を表す列挙型DeviceStateの定義
enum DeviceState {
    Off, On, Sleep
};

// デバイスの状態を表す変数stateの宣言
DeviceState state = On;

// stateの値に応じた処理
switch (state) {
    case Off:
        // デバイスがオフの場合の処理
        break;
    case On:
        // デバイスがオンの場合の処理
        break;
    case Sleep:
        // デバイスがスリープ状態の場合の処理
        break;
}

この例では、DeviceState 列挙型を使ってデバイスの状態を表しています。

switch 文を使って、state 変数の値に基づいて異なる処理を実行しています。

このように列挙型を使用することで、複数の状態を明確に管理し、プログラムの制御を容易にします。

○サンプルコード3:列挙型を使ったスイッチ文

最後に、列挙型をスイッチ文と組み合わせた例を紹介します。

下記のコードでは、方向を表す列挙型 Direction を定義し、スイッチ文でその値に応じた処理を行っています。

// 方向を表す列挙型Directionの定義
enum Direction {
    Up, Down, Left, Right
};

// 方向を表す変数dirの宣言
Direction dir = Up;

// dirの値に応じた処理
switch (dir) {
    case Up:
        // 上方向の場合の処理
        break;
    case Down:
        // 下方向の場合の処理
        break;
    case Left:
        // 左方向の場合の処理
        break;
    case Right:
        // 右方向の場合の処理
        break;
}

このコードでは、Direction 列挙型を使って4つの方向を表しています。

スイッチ文を使うことで、dir 変数の値に応じて異なる方向の処理を実行できます。

これにより、プログラムの流れを簡潔にし、方向に関連する処理を効率的に管理できます。

●列挙型の応用例とサンプルコード

C++における列挙型は基本的な使い方だけでなく、さまざまな応用が可能です。

ここでは、列挙型をより高度に活用するためのサンプルコードをいくつか紹介します。

これらの例は、列挙型をプログラムのさまざまなシナリオで効果的に使う方法を表しています。

○サンプルコード4:列挙型を使ったクラスのメンバ

まず、列挙型をクラスのメンバとして使う例を見てみましょう。

下記のコードでは、列挙型をクラス内で定義し、それをクラスのメンバ変数として使用しています。

class Car {
public:
    // Carクラス内で定義した列挙型EngineState
    enum EngineState {
        Off, On, Idle
    };

    // EngineState型のメンバ変数
    EngineState engineState;

    // コンストラクタ
    Car() : engineState(Off) {}

    // エンジンの状態を設定するメソッド
    void setEngineState(EngineState state) {
        engineState = state;
    }

    // 現在のエンジンの状態を取得するメソッド
    EngineState getEngineState() const {
        return engineState;
    }
};

// クラスの使用例
int main() {
    Car myCar;
    myCar.setEngineState(Car::On);
    if (myCar.getEngineState() == Car::Idle) {
        // エンジンがアイドル状態の時の処理
    }
}

このコードでは、Car クラスに EngineState という列挙型を定義し、エンジンの状態を表しています。

列挙型をクラスのメンバ変数として使用することで、そのクラスの状態を明確に表現することができます。

○サンプルコード5:列挙型と静的メンバ関数の組み合わせ

次に、列挙型と静的メンバ関数を組み合わせた例を見てみましょう。

下記のコードでは、列挙型の値に基づいて特定の処理を行う静的メンバ関数を定義しています。

class TrafficLight {
public:
    // 信号の状態を表す列挙型
    enum LightState {
        Red, Yellow, Green
    };

    // 信号の状態に応じたアクションを行う静的メンバ関数
    static void takeAction(LightState state) {
        switch (state) {
            case Red:
                // 赤信号の時の処理
                break;
            case Yellow:
                // 黄信号の時の処理
                break;
            case Green:
                // 緑信号の時の処理
                break;
        }
    }
};

// 静的メンバ関数の使用例
int main() {
    TrafficLight::takeAction(TrafficLight::Green);
    // 緑信号の時のアクションが実行される
}

このコードでは、TrafficLight クラスに LightState という列挙型を定義し、信号の状態を表しています。

静的メンバ関数 takeAction は、この列挙型の値に基づいて異なるアクションを実行します。

○サンプルコード6:列挙型の範囲for文での使用

C++では範囲for文を使用して、列挙型の全ての値に対して処理を行うことができます。

下記のサンプルコードでは、日曜日から土曜日までの曜日を表す列挙型を定義し、範囲for文を使用して各曜日に対する処理を実行しています。

// 曜日を表す列挙型Day
enum Day {
    Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, DayCount
};

// 曜日に対する処理を実行する関数
void processDay(Day day) {
    // 曜日に応じた処理
    // 例: cout << "Processing " << day << endl;
}

int main() {
    for (Day day = Sunday; day < DayCount; day = static_cast<Day>(day + 1)) {
        processDay(day);
    }
}

このコードでは、Day 列挙型を使用して曜日を表現し、for文を用いて各曜日に対する処理を行っています。

この方法は、列挙型の値を繰り返し処理する際に非常に有効です。

○サンプルコード7:列挙型を使ったビットフィールド

列挙型はビットフィールドとしても使用することができます。

ビットフィールドを使うと、複数のフラグを単一の変数で効率的に管理することができます。

下記のサンプルコードでは、様々な機能のオン・オフを表す列挙型を定義し、ビット演算を使用してフラグの管理を行っています。

// 機能のオン・オフを表す列挙型Feature
enum Feature {
    None     = 0,
    Feature1 = 1 << 0, // 1
    Feature2 = 1 << 1, // 2
    Feature3 = 1 << 2, // 4
    Feature4 = 1 << 3  // 8
};

int main() {
    // Featureの組み合わせを保持する変数
    int features = Feature1 | Feature3;

    // 特定の機能がオンになっているかをチェック
    if (features & Feature1) {
        // Feature1がオンの場合の処理
    }

    // 特定の機能をオフにする
    features &= ~Feature3;

    // 新たな機能を追加
    features |= Feature4;
}

このコードでは、ビット演算を使ってfeatures変数に複数のFeature値を組み合わせて保持し、個々のフラグの状態を管理しています。

ビットフィールドは、限られたメモリを効率的に使用する場合に特に有用です。

●列挙型の注意点

C++で列挙型を使用する際には、いくつかの注意点があります。

これらを理解しておくことは、プログラムのバグを避けるために重要です。

特に型安全性とスコープ、名前の衝突に関する問題は、列挙型を使用する上でよく考慮すべき点です。

○型安全について

列挙型は基本的に整数型として扱われますが、型安全性に関しては注意が必要です。

C++では、列挙型の値を別の型の変数に暗黙的に変換することが可能ですが、これにより意図しないバグを引き起こす可能性があります。

例えば、下記のコードを見てください。

enum Color {
    Red, Green, Blue
};

int main() {
    Color color = Red;
    int number = color; // 列挙型からint型への暗黙的な変換
}

この例では、Color 型の変数 color から int 型の変数 number へ暗黙的に変換されています。

これは意図した動作かもしれませんが、型安全性を損なうため、バグの原因となり得ます。

型安全性を高めるためには、明示的な型変換を行うか、より強い型の概念を持つ enum class を使用することを検討してください。

○列挙型のスコープと名前の衝突

通常の enum では、列挙される各値が列挙型のスコープ外に置かれます。

これにより、名前の衝突が起こり得ます。

例えば、下記のコードでは、名前の衝突が発生する可能性があります。

enum Color {
    Red, Green, Blue
};

enum Feeling {
    Happy, Sad, Red // Redが既にColorで定義されているため、衝突する
};

この例では、ColorFeeling の両方で Red が定義されているため、名前の衝突が発生します。

このような問題を避けるためには、enum class を使用すると良いでしょう。

enum class では、列挙される各値がその列挙型のスコープ内に限定されるため、名前の衝突を防ぐことができます。

enum class Color {
    Red, Green, Blue
};

enum class Feeling {
    Happy, Sad, Red // 列挙型のスコープが異なるため、衝突しない
};

以上のように、列挙型を使用する際は、型安全性やスコープ、名前の衝突に注意を払うことが重要です。

●列挙型のカスタマイズ方法

C++における列挙型は、基本的な使い方の他にも様々なカスタマイズが可能です。

これにより、特定の要件に合わせた柔軟なプログラミングが実現できます。

ここでは、クラス内での列挙型の使用と列挙型の値を拡張する方法について見ていきましょう。

○クラス内での列挙型の使用

クラス内で列挙型を定義することにより、そのクラス固有の状態やタイプを表現することができます。

下記のサンプルコードでは、車両の状態を表す列挙型をクラス内に定義し、それをクラスのメンバ変数として利用しています。

class Car {
public:
    // 車両の状態を表す列挙型
    enum class State {
        Off, Idle, Driving
    };

    // 車両の状態を管理するメンバ変数
    State currentState;

    // コンストラクタで初期状態を設定
    Car() : currentState(State::Off) {}

    // 状態の変更メソッド
    void changeState(State newState) {
        currentState = newState;
    }

    // 現在の状態を取得するメソッド
    State getState() const {
        return currentState;
    }
};

// クラスの使用例
int main() {
    Car myCar;
    myCar.changeState(Car::State::Driving);
    // ここでmyCarの状態がDrivingになっている
}

この例では、State 列挙型を Car クラス内で定義し、車両の状態を管理しています。

このように、列挙型をクラス内で使用することで、クラスの機能を明確にし、コードの可読性を高めることができます。

○列挙型の拡張とカスタム値

標準の列挙型では、値は自動的に0から始まる整数で割り当てられますが、カスタム値を割り当てることも可能です。

これにより、特定の意味を持つ数値や、既存のシステムとの互換性を持たせることができます。

下記の例では、特定の数値を列挙型の各値に割り当てています。

enum ErrorCode {
    None = 0,
    NetworkError = 100,
    DiskError = 200,
    UnknownError = 999
};

// エラーコードの使用例
void handleError(ErrorCode code) {
    switch (code) {
        case NetworkError:
            // ネットワークエラーの処理
            break;
        case DiskError:
            // ディスクエラーの処理
            break;
        default:
            // その他のエラーの処理
            break;
    }
}

このコードでは、ErrorCode 列挙型にカスタム値を割り当て、各種エラーを表現しています。

このように、列挙型の値に特定の数値を割り当てることで、プログラムの柔軟性と明確性を向上させることができます。

まとめ

この記事では、C++における列挙型の基本的な使用方法から応用例、注意点、カスタマイズ方法までを詳細に解説しました。

列挙型はプログラム内での明確な状態管理や定数の定義に非常に有効であり、enumenum class の適切な使用によってプログラムの可読性や安全性を高めることができます。

本記事を通じて、C++における列挙型の理解を深め、より効果的なプログラミングを行うための一助となれば幸いです。