条件分岐

[C++] switch文を使わないほうがいいケースを紹介

C++でswitch文を使わないほうがいいケースは、条件が複雑で可読性が低下する場合や、動的な値に基づく処理が必要な場合です。

例えば、条件が範囲や複数の値に依存する場合、if-else文や関数ポインタ、std::mapなどを使う方が柔軟でメンテナンス性が高まります。

また、switch文は整数型や列挙型に限定されるため、文字列や浮動小数点数を条件にしたい場合には不適切です。

switch文を使わないほうがいいケースとは

C++のswitch文は、特定の値に基づいて異なる処理を実行するための便利な構文ですが、いくつかのケースでは使用を避けた方が良い場合があります。

以下に、switch文を使わない方が良い理由やケースをいくつか挙げます。

1. 複雑な条件分岐が必要な場合

switch文は、単純な値の比較には適していますが、複雑な条件分岐には向いていません。

例えば、範囲や複数の条件を組み合わせた場合には、if文を使用する方が明確で可読性が高くなります。

#include <iostream>
int main() {
    int score = 85;
    if (score >= 90) {
        std::cout << "優秀です" << std::endl; // 優秀なスコア
    } else if (score >= 75) {
        std::cout << "良いです" << std::endl; // 良いスコア
    } else {
        std::cout << "改善が必要です" << std::endl; // 改善が必要なスコア
    }
    return 0;
}
良いです

2. 値が連続していない場合

switch文は、連続した整数値や列挙型に対しては効果的ですが、値が不連続な場合には、if文を使った方が効率的です。

switch文では、各ケースを個別に記述する必要があり、冗長になることがあります。

#include <iostream>
int main() {
    char grade = 'B';
    if (grade == 'A') {
        std::cout << "優秀" << std::endl; // 優秀
    } else if (grade == 'B') {
        std::cout << "良好" << std::endl; // 良好
    } else if (grade == 'C') {
        std::cout << "普通" << std::endl; // 普通
    } else {
        std::cout << "不合格" << std::endl; // 不合格
    }
    return 0;
}
良好

3. デフォルトケースが必要な場合

switch文では、デフォルトケースを用意することができますが、if文を使用することで、より柔軟に条件を設定できます。

特に、デフォルト以外の条件が多い場合には、if文の方が適しています。

#include <iostream>
int main() {
    int day = 8; // 1から7の範囲外の値
    if (day == 1) {
        std::cout << "月曜日" << std::endl; // 月曜日
    } else if (day == 2) {
        std::cout << "火曜日" << std::endl; // 火曜日
    } else if (day == 3) {
        std::cout << "水曜日" << std::endl; // 水曜日
    } else if (day == 4) {
        std::cout << "木曜日" << std::endl; // 木曜日
    } else if (day == 5) {
        std::cout << "金曜日" << std::endl; // 金曜日
    } else if (day == 6) {
        std::cout << "土曜日" << std::endl; // 土曜日
    } else if (day == 7) {
        std::cout << "日曜日" << std::endl; // 日曜日
    } else {
        std::cout << "無効な日" << std::endl; // 無効な日
    }
    return 0;
}
無効な日

4. 可読性が求められる場合

switch文は、ケースが多くなると可読性が低下することがあります。

特に、各ケースに異なる処理が必要な場合、if文を使った方がコードの意図が明確になります。

#include <iostream>
int main() {
    int option = 2;
    if (option == 1) {
        std::cout << "オプション1が選択されました" << std::endl; // オプション1
    } else if (option == 2) {
        std::cout << "オプション2が選択されました" << std::endl; // オプション2
    } else if (option == 3) {
        std::cout << "オプション3が選択されました" << std::endl; // オプション3
    } else {
        std::cout << "無効なオプション" << std::endl; // 無効なオプション
    }
    return 0;
}
オプション2が選択されました

5. 例外処理が必要な場合

switch文は、例外処理を行うことができません。

例外が発生する可能性がある場合には、try-catch文を使用する方が適切です。

#include <iostream>
#include <stdexcept>
int main() {
    int value = -1;
    try {
        if (value < 0) {
            throw std::invalid_argument("無効な値です"); // 例外をスロー
        }
        std::cout << "値は: " << value << std::endl; // 正常な値
    } catch (const std::invalid_argument& e) {
        std::cout << e.what() << std::endl; // 例外メッセージを表示
    }
    return 0;
}
無効な値です

switch文の代替手段

C++において、switch文の代わりに使用できるさまざまな構文や手法があります。

これらの代替手段は、特定の状況や要件に応じて、より適切な選択肢となることがあります。

以下に、主な代替手段を紹介します。

1. if-else文

if-else文は、条件に基づいて異なる処理を実行するための基本的な構文です。

複雑な条件分岐や範囲のチェックが必要な場合に特に有効です。

#include <iostream>
int main() {
    int number = 3;
    if (number == 1) {
        std::cout << "一" << std::endl; // 一
    } else if (number == 2) {
        std::cout << "二" << std::endl; // 二
    } else if (number == 3) {
        std::cout << "三" << std::endl; // 三
    } else {
        std::cout << "無効な数" << std::endl; // 無効な数
    }
    return 0;
}

2. 関数ポインタ

関数ポインタを使用することで、特定の条件に基づいて異なる関数を呼び出すことができます。

これにより、コードの可読性と再利用性が向上します。

#include <iostream>
void option1() {
    std::cout << "オプション1が選択されました" << std::endl; // オプション1
}
void option2() {
    std::cout << "オプション2が選択されました" << std::endl; // オプション2
}
void option3() {
    std::cout << "オプション3が選択されました" << std::endl; // オプション3
}
int main() {
    void (*funcPtr[3])() = {option1, option2, option3}; // 関数ポインタの配列
    int choice = 1; // 選択肢
    if (choice >= 1 && choice <= 3) {
        funcPtr[choice - 1](); // 選択された関数を呼び出す
    } else {
        std::cout << "無効な選択" << std::endl; // 無効な選択
    }
    return 0;
}
オプション1が選択されました

3. std::mapを使用したディスパッチ

C++の標準ライブラリに含まれるstd::mapを使用することで、キーに基づいて異なる処理を実行することができます。

これにより、動的に条件を追加することが可能になります。

#include <iostream>
#include <map>
#include <functional>
void optionA() {
    std::cout << "オプションAが選択されました" << std::endl; // オプションA
}
void optionB() {
    std::cout << "オプションBが選択されました" << std::endl; // オプションB
}
int main() {
    std::map<int, std::function<void()>> options; // オプションのマップ
    options[1] = optionA; // オプションA
    options[2] = optionB; // オプションB
    int choice = 2; // 選択肢
    if (options.find(choice) != options.end()) {
        options[choice](); // 選択されたオプションを呼び出す
    } else {
        std::cout << "無効な選択" << std::endl; // 無効な選択
    }
    return 0;
}
オプションBが選択されました

4. 列挙型とマップの組み合わせ

列挙型を使用して、選択肢を明示的に定義し、マップと組み合わせることで、より安全で可読性の高いコードを実現できます。

#include <iostream>
#include <map>
#include <functional>
enum class Option {
    Option1,
    Option2,
    Option3
};
void handleOption1() {
    std::cout << "オプション1が選択されました" << std::endl; // オプション1
}
void handleOption2() {
    std::cout << "オプション2が選択されました" << std::endl; // オプション2
}
void handleOption3() {
    std::cout << "オプション3が選択されました" << std::endl; // オプション3
}
int main() {
    std::map<Option, std::function<void()>> optionMap; // オプションのマップ
    optionMap[Option::Option1] = handleOption1; // オプション1
    optionMap[Option::Option2] = handleOption2; // オプション2
    optionMap[Option::Option3] = handleOption3; // オプション3
    Option selectedOption = Option::Option2; // 選択されたオプション
    if (optionMap.find(selectedOption) != optionMap.end()) {
        optionMap[selectedOption](); // 選択されたオプションを呼び出す
    } else {
        std::cout << "無効な選択" << std::endl; // 無効な選択
    }
    return 0;
}
オプション2が選択されました

5. 状態パターン

状態パターンを使用することで、オブジェクトの状態に応じて異なる処理を実行することができます。

これにより、コードの拡張性が向上します。

#include <iostream>
class State {
public:
    virtual void handle() = 0; // 状態の処理
};
class StateA : public State {
public:
    void handle() override {
        std::cout << "状態Aの処理" << std::endl; // 状態Aの処理
    }
};
class StateB : public State {
public:
    void handle() override {
        std::cout << "状態Bの処理" << std::endl; // 状態Bの処理
    }
};
class Context {
private:
    State* state; // 現在の状態
public:
    void setState(State* newState) {
        state = newState; // 状態を設定
    }
    void request() {
        state->handle(); // 現在の状態の処理を実行
    }
};
int main() {
    Context context;
    StateA stateA;
    StateB stateB;
    context.setState(&stateA); // 状態Aを設定
    context.request(); // 状態Aの処理を実行
    context.setState(&stateB); // 状態Bを設定
    context.request(); // 状態Bの処理を実行
    return 0;
}
状態Aの処理
状態Bの処理

switch文を使うべきケース

C++のswitch文は、特定の条件に基づいて異なる処理を実行するための便利な構文です。

以下に、switch文を使うべき具体的なケースをいくつか紹介します。

1. 整数や列挙型の値に基づく分岐

switch文は、整数や列挙型の値に基づく分岐に非常に適しています。

特に、値が連続している場合には、可読性が高くなります。

#include <iostream>
enum class Color {
    Red,
    Green,
    Blue
};
int main() {
    Color color = Color::Green; // 色の選択
    switch (color) {
        case Color::Red:
            std::cout << "赤色です" << std::endl; // 赤色
            break;
        case Color::Green:
            std::cout << "緑色です" << std::endl; // 緑色
            break;
        case Color::Blue:
            std::cout << "青色です" << std::endl; // 青色
            break;
        default:
            std::cout << "無効な色" << std::endl; // 無効な色
            break;
    }
    return 0;
}
緑色です

2. ケースが多い場合

switch文は、複数のケースがある場合に特に有効です。

if-else文に比べて、可読性が向上し、各ケースを明確に分けることができます。

#include <iostream>
int main() {
    int day = 5; // 曜日を表す数値
    switch (day) {
        case 1:
            std::cout << "月曜日" << std::endl; // 月曜日
            break;
        case 2:
            std::cout << "火曜日" << std::endl; // 火曜日
            break;
        case 3:
            std::cout << "水曜日" << std::endl; // 水曜日
            break;
        case 4:
            std::cout << "木曜日" << std::endl; // 木曜日
            break;
        case 5:
            std::cout << "金曜日" << std::endl; // 金曜日
            break;
        case 6:
            std::cout << "土曜日" << std::endl; // 土曜日
            break;
        case 7:
            std::cout << "日曜日" << std::endl; // 日曜日
            break;
        default:
            std::cout << "無効な日" << std::endl; // 無効な日
            break;
    }
    return 0;
}
金曜日

3. デフォルトケースが必要な場合

switch文では、デフォルトケースを用意することができ、予期しない値に対する処理を簡単に追加できます。

これにより、エラーハンドリングが容易になります。

#include <iostream>
int main() {
    int option = 4; // 選択肢
    switch (option) {
        case 1:
            std::cout << "オプション1が選択されました" << std::endl; // オプション1
            break;
        case 2:
            std::cout << "オプション2が選択されました" << std::endl; // オプション2
            break;
        case 3:
            std::cout << "オプション3が選択されました" << std::endl; // オプション3
            break;
        default:
            std::cout << "無効なオプション" << std::endl; // 無効なオプション
            break;
    }
    return 0;
}
無効なオプション

4. 同じ処理を複数のケースで行う場合

switch文では、複数のケースをまとめて同じ処理を行うことができます。

これにより、冗長なコードを避けることができます。

#include <iostream>
int main() {
    char grade = 'B'; // 成績
    switch (grade) {
        case 'A':
        case 'B':
            std::cout << "良い成績です" << std::endl; // 良い成績
            break;
        case 'C':
            std::cout << "普通の成績です" << std::endl; // 普通の成績
            break;
        default:
            std::cout << "不合格です" << std::endl; // 不合格
            break;
    }
    return 0;
}
良い成績です

5. 列挙型を使用する場合

列挙型を使用することで、switch文の可読性が向上し、意図が明確になります。

特に、状態やオプションを明示的に定義する場合に有効です。

#include <iostream>
enum class Status {
    Start,
    Processing,
    End
};
int main() {
    Status currentStatus = Status::Processing; // 現在の状態
    switch (currentStatus) {
        case Status::Start:
            std::cout << "処理開始" << std::endl; // 処理開始
            break;
        case Status::Processing:
            std::cout << "処理中" << std::endl; // 処理中
            break;
        case Status::End:
            std::cout << "処理終了" << std::endl; // 処理終了
            break;
        default:
            std::cout << "無効な状態" << std::endl; // 無効な状態
            break;
    }
    return 0;
}
処理中

まとめ

この記事では、C++におけるswitch文の使用に関するさまざまなケースを振り返りました。

特に、switch文を使うべき状況や、逆に避けるべきケースについて具体的な例を通じて解説しました。

これを参考にして、プログラムの可読性や保守性を向上させるために、適切な条件分岐の手法を選択してみてください。

関連記事

Back to top button