Bài 4.4: Bài tập thực hành rẽ nhánh switch-case nâng cao trong C++

Chào mừng bạn đến với chuỗi bài viết về C++ của FullhouseDev! Hôm nay, chúng ta sẽ cùng nhau đào sâu hơn vào một trong những cấu trúc điều khiển cơ bản nhưng cực kỳ mạnh mẽ và linh hoạt: câu lệnh switch-case.

Ở các bài trước, có thể bạn đã làm quen với cách sử dụng if-else if-else để xử lý nhiều điều kiện. switch-case là một lựa chọn thay thế tuyệt vời khi bạn cần so sánh một biến duy nhất với nhiều giá trị hằng số khác nhau. Nó giúp code của bạn trông gọn gàng, dễ đọc và trong nhiều trường hợp, có thể tối ưu hơn về hiệu suất so với chuỗi if-else if dài.

Bài viết này không chỉ dừng lại ở cú pháp cơ bản, mà chúng ta sẽ cùng nhau thực hành các tình huống phức tạp hơn một chút, nơi mà việc áp dụng switch-case một cách khéo léo sẽ mang lại hiệu quả bất ngờ. Hãy cùng bắt đầu nào!

Nhắc Lại Ngắn Gọn Về switch-case Cơ Bản

Trước khi đi vào "nâng cao", hãy cùng điểm lại cấu trúc cơ bản của switch-case:

switch (expression) {
    case value1:
        // Code thực thi khi expression == value1
        // ...
        break; // Rất quan trọng để thoát khỏi switch
    case value2:
        // Code thực thi khi expression == value2
        // ...
        break;
    // ... nhiều case khác ...
    default:
        // Code thực thi khi expression không khớp với bất kỳ case nào ở trên
        // ...
        break; // break ở default là tùy chọn, nhưng nên dùng để đồng nhất
}
  • expression: Biểu thức hoặc biến mà bạn muốn so sánh. Kết quả của nó phải là một kiểu dữ liệu nguyên (integer type), ký tự (char), hoặc enum. Kể từ C++11, bạn có thể dùng enum class.
  • case valueN:: valueN là các hằng số (literal) hoặc biểu thức hằng (constant expression) mà bạn muốn so sánh expression với. Các giá trị này phải duy nhất.
  • break;: Dừng việc thực thi bên trong switch và thoát ra khỏi khối switch. Đây là điểm thường gây lỗi nếu quên!
  • default:: Khối code được thực thi khi expression không khớp với bất kỳ case nào. Nó là tùy chọn và có thể đặt ở bất kỳ đâu trong khối switch (nhưng thường đặt ở cuối).

Lưu ý quan trọng: Nếu bạn quên break, code sẽ "rơi xuống" (fall through) và thực thi các case tiếp theo cho đến khi gặp break hoặc kết thúc khối switch. Đôi khi đây là hành vi mong muốn (chúng ta sẽ xem ví dụ sau), nhưng thường thì đó là một lỗi logic.

Thực Hành "Nâng Cao" Với switch-case

Vậy thế nào là "nâng cao" ở đây? Không phải là cú pháp phức tạp, mà là cách chúng ta áp dụng switch-case để giải quyết các vấn đề thực tế một cách hiệu quả và thanh lịch hơn, đặc biệt là khi kết hợp nó với các cấu trúc khác hoặc xử lý các loại dữ liệu khác nhau một cách gián tiếp.

Hãy cùng đi qua một vài ví dụ minh họa sinh động:

Ví dụ 1: Xử lý Menu Tương Tác Đơn Giản

Một trong những ứng dụng phổ biến nhất của switch-case là xử lý các lựa chọn từ menu. Thay vì dùng nhiều if-else if, switch làm cho code trở nên cực kỳ rõ ràng.

#include <iostream>

int main() {
    cout << "--- MENU CHUONG TRINH ---" << endl;
    cout << "1. Them moi" << endl;
    cout << "2. Hien thi" << endl;
    cout << "3. Tim kiem" << endl;
    cout << "4. Thoat" << endl;
    cout << "-------------------------" << endl;
    cout << "Nhap lua chon cua ban: ";

    int lc;
    cin >> lc;

    switch (lc) {
        case 1:
            cout << "* Ban da chon: Them moi *" << endl;
            break;
        case 2:
            cout << "* Ban da chon: Hien thi *" << endl;
            break;
        case 3:
            cout << "* Ban da chon: Tim kiem *" << endl;
            break;
        case 4:
            cout << "* Ban da chon: Thoat chuong trinh *" << endl;
            break;
        default:
            cout << "* Lua chon khong hop le. Vui long nhap lai. *" << endl;
            break;
    }

    return 0;
}

Output:

--- MENU CHUONG TRINH ---
1. Them moi
2. Hien thi
3. Tim kiem
4. Thoat
-------------------------
Nhap lua chon cua ban: 2
* Ban da chon: Hien thi *
Ví dụ 2: Kết hợp với Vòng Lặp để Tạo Menu Chạy Liên Tục

Để làm cho menu ở ví dụ 1 hữu dụng hơn, chúng ta thường đặt switch bên trong một vòng lặp (ví dụ: while) để chương trình chạy liên tục cho đến khi người dùng chọn thoát.

#include <iostream>

int main() {
    int lc = 0;

    while (lc != 4) {
        cout << "\n--- MENU CHUONG TRINH ---" << endl;
        cout << "1. Them moi" << endl;
        cout << "2. Hien thi" << endl;
        cout << "3. Tim kiem" << endl;
        cout << "4. Thoat" << endl;
        cout << "-------------------------" << endl;
        cout << "Nhap lua chon cua ban: ";

        cin >> lc;

        switch (lc) {
            case 1:
                cout << "* Ban da chon: Them moi *" << endl;
                break;
            case 2:
                cout << "* Ban da chon: Hien thi *" << endl;
                break;
            case 3:
                cout << "* Ban da chon: Tim kiem *" << endl;
                break;
            case 4:
                cout << "* Ban da chon: Thoat chuong trinh *" << endl;
                break;
            default:
                cout << "* Lua chon khong hop le. Vui long nhap lai. *" << endl;
                break;
        }
    }

    cout << "Chuong trinh da ket thuc. Tam biet!" << endl;

    return 0;
}

Output:

--- MENU CHUONG TRINH ---
1. Them moi
2. Hien thi
3. Tim kiem
4. Thoat
-------------------------
Nhap lua chon cua ban: 1
* Ban da chon: Them moi *

--- MENU CHUONG TRINH ---
1. Them moi
2. Hien thi
3. Tim kiem
4. Thoat
-------------------------
Nhap lua chon cua ban: 9
* Lua chon khong hop le. Vui long nhap lai. *

--- MENU CHUONG TRINH ---
1. Them moi
2. Hien thi
3. Tim kiem
4. Thoat
-------------------------
Nhap lua chon cua ban: 4
* Ban da chon: Thoat chuong trinh *
Chuong trinh da ket thuc. Tam biet!
Ví dụ 3: Sử dụng Ký Tự (char) trong switch-case

Không chỉ số nguyên, switch-case cũng hoạt động tuyệt vời với kiểu ký tự (char). Điều này hữu ích khi bạn muốn người dùng nhập các lệnh đơn giản bằng chữ cái (ví dụ: 'a' để thêm, 'q' để thoát).

#include <iostream>
#include <cctype>

int main() {
    char l;

    cout << "Nhap lenh ('A' them, 'D' xoa, 'Q' thoat): ";
    cin >> l;

    l = tolower(l);

    switch (l) {
        case 'a':
            cout << "Lenh them moi duoc thuc thi." << endl;
            break;
        case 'd':
            cout << "Lenh xoa duoc thuc thi." << endl;
            break;
        case 'q':
            cout << "Dang thoat chuong trinh." << endl;
            break;
        default:
            cout << "Lenh khong hop le." << endl;
            break;
    }

    return 0;
}

Output:

Nhap lenh ('A' them, 'D' xoa, 'Q' thoat): D
Lenh xoa duoc thuc thi.
Ví dụ 4: Xử lý Nhiều Case Cùng Một Khối Lệnh (Fall-through có chủ đích)

Như đã đề cập, việc quên break có thể gây lỗi. Tuy nhiên, đôi khi bạn cố ý để code "rơi xuống" (fall-through) để thực hiện cùng một khối lệnh cho nhiều case. Điều này hữu ích khi nhiều giá trị đầu vào dẫn đến cùng một hành động.

Lưu ý: Khi sử dụng fall-through có chủ đích, nên thêm bình luận để người đọc code hiểu rằng đây không phải là lỗi quên break.

#include <iostream>

int main() {
    int ngay;

    cout << "Nhap mot ngay trong tuan (1-7): ";
    cin >> ngay;

    switch (ngay) {
        case 1:
        case 7:
            cout << "Day la ngay cuoi tuan! Rat tuyet!" << endl;
            break;
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
            cout << "Day la ngay trong tuan. Co gang lam viec nao!" << endl;
            break;
        default:
            cout << "Du lieu nhap khong hop le." << endl;
            break;
    }

    return 0;
}

Output:

Nhap mot ngay trong tuan (1-7): 1
Day la ngay cuoi tuan! Rat tuyet!
Ví dụ 5: Xử lý Giá Trị Nằm Trong Khoảng (Sử dụng switch-case một cách gián tiếp)

switch-case trong C++ không hỗ trợ trực tiếp các khoảng giá trị (như case 1..10:). Tuy nhiên, chúng ta có thể "lách" bằng cách biến đổi giá trị đầu vào thành một giá trị rời rạc (discrete value) trước khi đưa vào switch.

Ví dụ: Phân loại điểm số học sinh theo thang 10. Chúng ta có thể chia điểm cho 10 để nhóm chúng lại.

#include <iostream>

int main() {
    int d;

    cout << "Nhap diem so (0-10): ";
    cin >> d;

    if (d < 0 || d > 10) {
        cout << "Diem khong hop le!" << endl;
    } else {
        int loai;
        if (d >= 9) {
            loai = 4;
        } else if (d >= 7) {
            loai = 3;
        } else if (d >= 5) {
            loai = 2;
        } else if (d >= 3) {
             loai = 1;
        }
        else {
            loai = 0;
        }

        switch (loai) {
            case 4:
                cout << "Xep loai: Gioi" << endl;
                break;
            case 3:
                cout << "Xep loai: Kha" << endl;
                break;
            case 2:
                cout << "Xep loai: Trung binh" << endl;
                break;
            case 1:
                cout << "Xep loai: Yeu" << endl;
                break;
            case 0:
                cout << "Xep loai: Kem" << endl;
                break;
        }
    }

    return 0;
}

Output:

Nhap diem so (0-10): 7
Xep loai: Kha
Ví dụ 6: Sử dụng Enum với switch-case

Sử dụng enum (hoặc enum class từ C++11) với switch-case là một thực hành tốt (best practice) giúp code dễ đọc, dễ bảo trìan toàn hơn (trình biên dịch có thể cảnh báo nếu bạn quên xử lý một giá trị enum nào đó trong switch).

#include <iostream>

enum class Tthai {
    Cho,
    XuLy,
    Xong,
    Loi
};

int main() {
    Tthai hienTai = Tthai::XuLy;

    switch (hienTai) {
        case Tthai::Cho:
            cout << "Trang thai: Dang cho xu ly." << endl;
            break;
        case Tthai::XuLy:
            cout << "Trang thai: Dang xu ly." << endl;
            break;
        case Tthai::Xong:
            cout << "Trang thai: Hoan thanh." << endl;
            break;
        case Tthai::Loi:
            cout << "Trang thai: That bai." << endl;
            break;
    }

    int n;
    cout << "Nhap trang thai (0: Cho, 1: XuLy, 2: Xong, 3: Loi): ";
    cin >> n;

    Tthai nguoiDung = static_cast<Tthai>(n);

    switch (nguoiDung) {
        case Tthai::Cho:
            cout << "Ban da nhap: Cho." << endl;
            break;
        case Tthai::XuLy:
            cout << "Ban da nhap: XuLy." << endl;
            break;
        case Tthai::Xong:
            cout << "Ban da nhap: Xong." << endl;
            break;
        case Tthai::Loi:
            cout << "Ban da nhap: Loi." << endl;
            break;
        default:
             cout << "Input trang thai khong hop le." << endl;
            break;
    }

    return 0;
}

Output:

Trang thai: Dang xu ly.
Nhap trang thai (0: Cho, 1: XuLy, 2: Xong, 3: Loi): 1
Ban da nhap: XuLy.

Các Lời Khuyên Khi Sử Dụng switch-case "Nâng Cao"

  1. Luôn luôn có default: Trừ khi bạn hoàn toàn chắc chắn rằng biến expression sẽ luôn khớp với một trong các case (ví dụ: xử lý tất cả các giá trị của một enum đã định nghĩa kỹ), hãy luôn bao gồm khối default để xử lý các trường hợp không mong muốn hoặc input không hợp lệ.
  2. Đừng quên break;: Đây là lỗi phổ biến nhất khi dùng switch. Hãy kiểm tra kỹ. Nếu bạn cố ý fall-through, hãy thêm bình luận (// fall-through hoặc tương tự) để làm rõ ý định.
  3. Giữ cho code trong case ngắn gọn: Mỗi khối case lý tưởng chỉ nên chứa một vài dòng lệnh hoặc gọi một hàm khác để thực hiện công việc phức tạp. Điều này giúp giữ cấu trúc switch sạch sẽ và dễ đọc.
  4. Sử dụng enum hoặc enum class: Đối với các tập hợp giá trị rời rạc có ý nghĩa (như trạng thái, loại đối tượng, mã lỗi), hãy định nghĩa chúng bằng enum và sử dụng trong switch.
  5. Hạn chế lồng switch: Mặc dù C++ cho phép, việc lồng các câu lệnh switch vào nhau có thể làm code rất khó hiểu và khó theo dõi. Thường có những cách tổ chức code tốt hơn cho các logic phức tạp.

Comments

There are no comments at the moment.