Mặc dù có thể xâu chuỗi nhiều câu lệnh if-else lại với nhau, nhưng điều này rất khó đọc code. Hãy xem xét chương trình sau:

/**
* Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

#include <iostream>
 
enum Colors
{
    COLOR_BLACK,
    COLOR_WHITE,
    COLOR_RED,
    COLOR_GREEN,
    COLOR_BLUE
};
 
void printColor(Colors color)
{
    if (color == COLOR_BLACK)
        std::cout << "Black";
    else if (color == COLOR_WHITE)
        std::cout << "White";
    else if (color == COLOR_RED)
        std::cout << "Red";
    else if (color == COLOR_GREEN)
        std::cout << "Green";
    else if (color == COLOR_BLUE)
        std::cout << "Blue";
    else
        std::cout << "Unknown";
}
 
int main()
{
    printColor(COLOR_GREEN);
 
    return 0;
}

Vì thực hiện chuỗi if-else trên cho một biến duy nhất(color) là rất phổ biến, C ++ cung cấp một toán tử phân nhánh có điều kiện thay thế được gọi là switch . Đây là chương trình tương tự như trên ở dạng switch:

/**
* Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

void printColor(Colors color)
{
    switch (color)
    {
        case COLOR_BLACK:
            std::cout << "Black";
            break;
        case COLOR_WHITE:
            std::cout << "White";
            break;
        case COLOR_RED:
            std::cout << "Red";
            break;
        case COLOR_GREEN:
            std::cout << "Green";
            break;
        case COLOR_BLUE:
            std::cout << "Blue";
            break;
        default:
            std::cout << "Unknown";
            break;
    }
}

Ý tưởng đằng sau các câu lệnh switch rất đơn giản: Ta sẽ có một biểu thức(Biến nào đó) trong câu lệnh switch được ước tính để tạo ra một giá trị và mỗi case sẽ so sánh với giá trị đó để xem nó có bằng nhau không(khớp nhau). Nếu một case nào đó nào đó bằng nhau, các câu lệnh sau case đó sẽ được thực thi. Nếu không có case nào bằng với giá trị được ước tính từ biểu thức(Biến nào đó), các câu lệnh sau nhãn default sẽ được thực thi (nếu nó tồn tại).

Do cách chúng triển khai nên các câu lệnh switch thường hiệu quả hơn các chuỗi câu lệnh if-else.

Chúng ta hãy xem xét từng khái niệm chi tiết hơn sau đây.

1. Khái niệm Switch

Chúng ta bắt đầu một câu lệnh switch bằng cách sử dụng từ khóa switch, theo sau là biểu thức(or biến) mà chúng ta muốn đánh giá. Thông thường biểu thức này chỉ là một biến duy nhất, nhưng nó có thể là một cái gì đó phức tạp hơn như nX + 2 hoặc nX - nY . Một hạn chế đối với biểu thức này là nó chỉ đánh giá các kiểu dữ liệu loại tích phân mà thôi (nghĩa là nó có thể đánh giá được các kiểu char, short, int, long, long long hoặc enum). Các biến dấu phẩy động(float, double), chuỗi và các loại không phải kiểu dữ liệu loại tích phân khác có thể không được sử dụng ở đây.

Theo câu lệnh switch, chúng ta khai báo một khối lệnh. Trong khối lệnh đó, chúng ta sử dụng các nhãn với từ khoá case để xác định tất cả các giá trị mà chúng ta muốn kiểm tra tính bằng nhau. Chúng ta có 2 loại nhãn khác nhau.

2. Các nhãn case

Loại nhãn đầu tiên là nhãn case , được khai báo bằng từ khóa case và theo sau là một biểu thức không đổi. Một biểu thức hằng là một biểu thức có một giá trị không đổi – nói cách khác, bằng chữ (chẳng hạn như 5), enum (chẳng hạn như COLOR_RED) hoặc biến không đổi (chẳng hạn như x, khi x được định nghĩa là một const int).

Biểu thức không đổi theo nhãn case được kiểm tra bằng với biểu thức theo từ khóa switch. Nếu chúng khớp thì code dưới nhãn case tương ứng được thực thi.

Điều đáng chú ý là tất cả các biểu thức nhãn case phải ước tính thành một giá trị duy nhất và không trùng nhau. Khi đó bạn không thể làm điều này:

switch (x)
{
    case 4:
    case 4:  // illegal -- already used value 4!
    case COLOR_BLUE: // illegal, COLOR_BLUE evaluates to 4!
}

Có thể có nhiều nhãn case để kiểm tra với một biển thức hay biến câu lệnh switch. Hàm sau sử dụng nhiều case để kiểm tra xem tham số ‘c’ có phải là chữ số ASCII không.

bool isDigit(char c)
{
    switch (c)
    {
        case '0': // if c is 0
        case '1': // or if c is 1
        case '2': // or if c is 2
        case '3': // or if c is 3
        case '4': // or if c is 4
        case '5': // or if c is 5
        case '6': // or if c is 6
        case '7': // or if c is 7
        case '8': // or if c is 8
        case '9': // or if c is 9
            return true; // then return true
        default:
            return false;
    }
}

Trong trường hợp c là một chữ số ASCII, câu lệnh case đầu tiên nếu khớp sẽ được thực thi, và hàm sẽ return true.

Lưu ý rằng bạn không cần phải sử dụng tính năng break nếu bạn sử dụng tính năng return (và ngược lại), 2 cái này điều sẽ khiến việc swich chấm dứt.

3. Nhãn default

Loại nhãn thứ hai là nhãn default (thường được gọi là trường hợp mặc định của chế độ default), được khai báo bằng từ khóa default. Code dưới nhãn này được thực thi nếu không có trường hợp nào khớp với biểu thức của câu lệnh switch. Nhãn default là có thể có or không và chỉ có thể có một nhãn default cho mỗi câu lệnh Switch. Nó cũng thường được khai báo ở nhãn cuối cùng trong khối switch, mặc dù điều này không thực sự cần thiết.

Trong ví dụ isDigit() ở trên, nếu c không phải là chữ số ASCII, trường hợp mặc định sẽ thực thi và trả về false.

4. Thực thi Switch

Một trong những điều khó nhất khi dùng các case là quá trình mà nó xử lý khi một trường hợp được khớp. Khi một trường hợp được khớp (hoặc mặc định được thực thi) thì việc thực thi bắt đầu tại câu lệnh đầu tiên theo nhãn đó và tiếp tục cho đến khi một trong các điều kiện kết thúc sau là đúng:
1) Kết thúc khối switch
2) Gặp một cậu lệnh return
3) Gặp một câu lệnh goto
4) Gặp một câu lệnh break
5) Một cái gì đó khác làm gián đoạn dòng xử lý bình thường của chương trình (ví dụ: một lệnh gọi để exit (), một ngoại lệ xảy ra, vũ trụ nổ tung, v.v.

Lưu ý rằng nếu không có điều kiện chấm dứt nào nào ở trên xảy ra thì các case tiếp theo sẽ được xử lý! Hãy xem xét đoạn trích sau:

/**
* Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

switch (2)
{
   case 1: // Does not match
       std::cout << 1 << '\n'; // skipped
   case 2: // Match!
       std::cout << 2 << '\n'; // Execution begins here
   case 3:
       std::cout << 3 << '\n'; // This is also executed
   case 4:
       std::cout << 4 << '\n'; // This is also executed
   default:
       std::cout << 5 << '\n'; // This is also executed
}

Đoạn trích này in kết quả:

2
3
4
5

Đây có lẽ không phải là những gì chúng ta muốn! Khi thực thi từ case này sang case khác, điều này được gọi là lướt qua từng case (Fall-through). Fall-through gần như không bao giờ được lập trình viên mong muốn, vì vậy trong trường hợp hiếm hoi, thông thường để lại một comment nói rằng việc lướt qua này là cố ý làm gì đó.

5. Câu lệnh break

Một câu lệnh break (được khai báo sử dụng từ khóa break ) cho trình biên dịch biết rằng chúng ta đã hoàn thành cái này (có thể dùng cho while, do while hoặc for loop). Sau khi gặp câu lệnh break thì việc thực thi khối lệnh switch sẽ kết thúc.

Hãy xem xét ví dụ cuối cùng của chúng ta với các câu lệnh break được chèn đúng cách:

/**
* Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
	
switch (2)
{
   case 1: // Does not match -- skipped
       std::cout << 1 << '\n';
       break;
   case 2: // Match!  Execution begins at the next statement
       std::cout << 2 << '\n'; // Execution begins here
       break; // Break terminates the switch statement
   case 3:
       std::cout << 3 << '\n';
       break;
   case 4:
       std::cout << 4 << '\n';
       break;
   default:
       std::cout << 5 << '\n';
       break;
}
// Execution resumes here

Bây giờ, khi case 2 được so sánh giống nhau với số 2 trên câu lệnh switch thì số nguyên 2 sẽ được xuất ra và câu lệnh break sẽ khiến khối lệnh switch kết thúc. Các case khác được bỏ qua.

Cảnh báo: Quên câu lệnh break ở cuối một xử lý case nào đó là một trong những lỗi C ++ phổ biến nhất được thực hiện!

6. Nhiều câu lệnh trong một khối switch

Với câu lệnh if , bạn chỉ có thể có một câu lệnh sau điều kiện if và câu lệnh đó được coi là câu lệnh ẩn bên trong một khối:

if (x > 10)
    std::cout << x << " is greater than 10\n"; // this line implicitly considered to be inside a block

Tuy nhiên, với các câu lệnh switch, bạn được phép có nhiều câu lệnh sau một nhãn case và chúng không được coi là nằm trong một khối ẩn.

switch (1)
{
    case 1:
        std::cout << 1;
        foo();
        std::cout << 2;
        break;
    default:
        std::cout << "default case\n";
        break;
}

Trong ví dụ trên, 4 câu lệnh giữa nhãn case và nhãn default là một phần của case nào đó. Có nghĩa là cứ 1 case sẽ có hơn 1 câu lệnh bên trong nó.

7. Khai báo biến và khởi tạo bên trong các câu lệnh case

Bạn có thể khai báo (nhưng không khởi tạo) các biến trong từng case, cả trước và sau nhãn case:

/**
* Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

switch (1)
{
    int a; // okay, declaration is allowed before the case labels
    int b = 5; // illegal, initialization is not allowed before the case labels
 
    case 1:
        int y; // okay, declaration is allowed within a case
        y = 4; // okay, this is an assignment
        break;
 
    case 2:
        y = 5; // okay, y was declared above, so we can use it here too
        break;
 
    case 3:
        int z = 4; // illegal, initialization is not allowed within a case
        break;
 
    default:
        std::cout << "default case" << std::endl;
        break;
}

Lưu ý rằng mặc dù biến y được định nghĩa trong case 1, nó cũng được sử dụng trong case 2. Điều đó có nghĩa là tất cả các câu lệnh bên trong khối switch là một phần của cùng một phạm vi. Do đó, một biến được định nghĩa trong một case có thể được sử dụng trong case khác, ngay cả khi trường hợp trong đó biến được định nghĩa không bao giờ được thực thi!

Điều này có vẻ hơi phản trực giác, vì vậy hãy xem xét tại sao. Khi bạn khái báo một biến cục bộ như là int y;, biến đó không được tạo tại thời điểm đó – nó thực sự được tạo ở đầu khối mà nó đã khai báo. Câu lệnh khai báo không cần thực thi – nó chỉ cho trình biên dịch biết rằng biến có thể được sử dụng sau đó. Vì vậy, với ý nghĩ đó, sẽ hơi kỳ lạ khi một biến được khai báo trong một câu lệnh case nào đó có thể được sử dụng trong câu lệnh case khác, ngay cả khi câu lệnh case khai báo biến đó không bao giờ được thực thi.

Tuy nhiên, việc khởi tạo các biến trực tiếp bên dưới nhãn case không được phép và sẽ gây ra lỗi biên dịch. Điều này là do việc khởi tạo một biến không yêu cầu thực thi và câu lệnh case có chứa khởi tạo có thể không được thực thi!

Nếu một trường hợp cần khai báo hoặc khởi tạo một biến mới, cách tốt nhất là khai báo nó bên trong một khối của câu lệnh case:

switch (1)
{
    case 1:
    { // note addition of block here
        int x = 4; // okay, variables can be initialized inside a block inside a case
        std::cout << x;
        break;
    }
    default:
        std::cout << "default case" << std::endl;
        break;
}

Quy tắc: Nếu xác định các biến được sử dụng trong câu lệnh case thì hãy thực hiện trong một khối lệnh bên trong case thích hợp.

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!