Enumクラスと強い型付け
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 80/104。
C++11では、従来のCスタイルの列挙型の欠点を解消するために、enum class(スコープを持つ列挙型とも呼ばれます)が導入されました。これらは、より強力な型安全性を提供し、列挙値の誤用を防ぎます。
従来の列挙型(enum)には問題があります。値が周囲のスコープに漏れ出し、整数に暗黙的に変換されるため、捉えにくいバグの原因となります:
// 従来の列挙型 - 問題あり
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // エラー!RedとGreenは既に定義されています
int x = Red; // 暗黙的にint型(0)に変換される
if (Red == 0) { } // コンパイル可能 - 列挙型とint型を比較している列挙型クラス(Enum classes)は、値をスコープ内に限定し、暗黙的な型変換を防ぐことで、これらの問題を解決します。
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設計と親和性が高く、コードの可読性を向上させ、実行時ではなくコンパイル時に型エラーを検出できるようにします。
チャレンジ
簡単列挙型クラス(enum classes)を使用して、異なる優先度レベルとタスクステータスを表現するタスク優先度システムを構築しましょう。これにより、列挙型クラスがどのように型安全性を提供し、従来の列挙型で発生しがちなバグを防ぐかを確認できます。
コードは以下の3つのファイルに分けて構成します:
TaskTypes.h: タスクシステム用の列挙型クラスを定義します。Priority列挙型クラスを作成し、値としてLow、Medium、High、Criticalを持たせます。基底型をintと指定し、それぞれの値を 1、2、3、4 とします。Status列挙型クラスを作成し、値としてPending、InProgress、Completedを持たせます。2つのヘルパー関数を用意します:
priorityToString(Priority p)— 優先度を文字列("Low"、"Medium"、"High"、または "Critical")として返します。statusToString(Status s)— ステータスを文字列("Pending"、"InProgress"、または "Completed")として返します。
Task.h: 定義した列挙型クラスを使用するTaskクラスを定義します。Taskクラスには、名前(string)、Priority、およびStatusを保持させます。コンストラクタは名前と優先度を受け取り、ステータスのデフォルト値はStatus::Pendingとします。以下のメソッドを実装してください:
setStatus(Status s)— タスクのステータスを更新します。getPriorityValue()—static_castを使用して、優先度の基底となる整数値を返します。display()— タスクを次の形式で出力します:[name] - Priority: [priority] (Value: [value]) - Status: [status]
main.cpp: 3つの入力を読み込みます:- タスク名(文字列)
- 整数としての優先度レベル(1=Low, 2=Medium, 3=High, 4=Critical)
- 整数としてのステータス(0=Pending, 1=InProgress, 2=Completed)
指定された名前と優先度で
Taskを作成します(static_castを使用して整数を適切なPriority列挙値に変換してください)。次に、3番目の入力に基づいてステータスを設定します。最後に、display()を呼び出してタスクの詳細を表示します。
例えば、入力が Fix bug、3、1 の場合:
Fix bug - Priority: High (Value: 3) - Status: InProgress入力が Write docs、1、2 の場合:
Write docs - Priority: Low (Value: 1) - Status: Completed入力が Deploy app、4、0 の場合:
Deploy app - Priority: Critical (Value: 4) - Status: Pending列挙型クラスによって、優先度とステータスの値が完全に分離されていることに注目してください。誤って Priority と Status を比較したり、一方を他方に代入したりすることはできません。整数と列挙値の間の変換に明示的な static_cast が必要になることで、コードの意図が明確になり、潜在的なバグを防ぐことができます。
チートシート
C++11では、従来のCスタイルの列挙型(enum)よりも強力な型安全性を提供するために、enum class(スコープを持つ列挙型)が導入されました。
従来の列挙型には、スコープの汚染や暗黙的な型変換といった問題があります。
// 従来の列挙型 - 問題あり
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // エラー!RedとGreenは既に定義されています
int x = Red; // int型に暗黙的に変換される
if (Red == 0) { } // コンパイル可能 - 列挙型とint型を比較しているenum classは、スコープ解決を必須とし、暗黙的な型変換を防止することで、これらの問題を解決します。
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 classと整数の間で変換を行うには、明示的なキャストを使用します:
// 整数からenum classへ
Priority p = static_cast<Priority>(3);
// enum classから整数へ
int value = static_cast<int>(p);自分で試してみよう
#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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス