Menu
Coddy logo textTech

Enumクラスと強い型付け

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 80/104。

C++11では、従来のCスタイルの列挙型の欠点に対処するため、enum classes(スコープ付き列挙型とも呼ばれます)が導入されました。これにより、より強い型安全性が提供され、列挙値の意図しない誤用が防止されます。

従来の列挙型には問題があります。値が周囲のスコープに漏れ出し、暗黙的に整数へ変換されるため、見つけにくいバグにつながります。

// 従来のenum - 問題あり
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green };  // エラー!Red と Green は既に定義されています

int x = Red;  // 暗黙的に int (0) に変換される
if (Red == 0) { }  // コンパイルされる - enum と int を比較

列挙型クラスは、値のスコープを限定し、暗黙の変換を防ぐことで、これらの問題を解決します。

enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green };  // OK - 競合なし

Color c = Color::Red;      // スコープ演算子を使用する必要がある
// int x = c;              // エラー! 暗黙の変換なし
int x = static_cast<int>(c);  // 明示的な変換が必要

// if (c == 0) { }         // エラー! intと比較できない
if (c == Color::Red) { }   // OK - 同じ型同士の比較

メモリを制御するために基底型を指定することもできます:

enum class Status : uint8_t {
    Inactive = 0,
    Active = 1,
    Pending = 2
};

Enum クラスは OOP の設計と相性がよく、コードをより読みやすくし、実行時ではなくコンパイル時に型エラーを検出できます。

challenge icon

チャレンジ

簡単

異なる優先度レベルとタスクの状態を表すために enum クラスを使用するタスク優先度システムを構築しましょう。これは、enum クラスが型安全性を提供し、従来の enum で発生するようなバグを防ぐ方法を示しています。

コードを3つのファイルに分けて整理します。

  • TaskTypes.h: タスクシステム用の enum クラスを定義します。

    Priority enum クラスを作成し、値として LowMediumHighCritical を設定します。基底型として int を指定し、それぞれの値を1、2、3、4とします。

    Status enum クラスを作成し、値として PendingInProgressCompleted を設定します。

    2つのヘルパー関数を用意します。

    • priorityToString(Priority p): 優先度を文字列("Low"、"Medium"、"High"、または "Critical")として返します
    • statusToString(Status s): 状態を文字列("Pending"、"InProgress"、または "Completed")として返します
  • Task.h: enum クラスを使用する Task クラスを定義します。

    Task には、名前(文字列)、PriorityStatus を保持させます。コンストラクターは名前と優先度を受け取り、状態のデフォルト値は Status::Pending とします。

    次のメソッドを実装します。

    • setStatus(Status s): タスクの状態を更新します
    • getPriorityValue(): static_cast を使用して、優先度の基底となる整数値を返します
    • display(): タスクを次の形式で出力します: [name] - Priority: [priority] (Value: [value]) - Status: [status]
  • main.cpp: 3つの入力を読み取ります。
    1. タスク名(文字列)
    2. 優先度レベル(整数)(1=Low、2=Medium、3=High、4=Critical)
    3. 状態(整数)(0=Pending、1=InProgress、2=Completed)

    指定された名前と優先度で Task を作成します(static_cast を使用して整数を適切な Priority enum 値に変換します)。次に、3番目の入力に基づいて状態を設定します。最後に display() を呼び出してタスクの詳細を表示します。

たとえば、入力が Fix bug31 の場合:

Fix bug - Priority: High (Value: 3) - Status: InProgress

入力が Write docs12 の場合:

Write docs - Priority: Low (Value: 1) - Status: Completed

入力が Deploy app40 の場合:

Deploy app - Priority: Critical (Value: 4) - Status: Pending

enum クラスによって、優先度と状態の値が完全に分離されていることに注目してください。PriorityStatus と誤って比較したり、一方をもう一方に代入したりすることはできません。整数と enum 値の間の変換に必要な明示的な static_cast によって、コードの意図が明確になり、見つけにくいバグを防止できます。

自分で試してみよう

#include <iostream>
#include <string>
#include "Task.h"

using namespace std;

int main() {
    // 入力を読み取る
    string taskName;
    int priorityInt;
    int statusInt;
    
    getline(cin, taskName);
    cin >> priorityInt;
    cin >> statusInt;
    
    // TODO: 指定された名前と優先度で Task を作成する
    // static_cast を使用して priorityInt を Priority 列挙型に変換する
    
    // TODO: statusInt に基づいてタスクのステータスを設定する
    // static_cast を使用して statusInt を Status 列挙型に変換する
    
    // TODO: display() を呼び出してタスクの詳細を表示する
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C++オンラインコンパイラ