C++ chứa khá nhiều kiểu dữ liệu. Nhưng những loại này không phải lúc nào cũng đủ cho những thứ chúng ta muốn làm. Vì vậy, C ++ chứa các khả năng cho phép lập trình viên tạo các kiểu dữ liệu của riêng họ. Những kiểu dữ liệu này được gọi là kiểu dữ liệu do người dùng định nghĩa.

Có lẽ kiểu dữ liệu do người dùng định nghĩa đơn giản nhất là kiểu enum. Một kiểu enum là kiểu dữ liệu trong đó mọi giá trị có thể được định nghĩa là hằng số ký hiệu. Kiểu enum được định nghĩa thông qua từ khóa enum. Hãy nhìn vào một ví dụ:

// Define a new enumeration named Color
enum Color
{
    // Here are the enumerators
    // These define all the possible values this type can hold
    // Each enumerator is separated by a comma, not a semicolon
    COLOR_BLACK,
    COLOR_RED,
    COLOR_BLUE,
    COLOR_GREEN,
    COLOR_WHITE,
    COLOR_CYAN,
    COLOR_YELLOW,
    COLOR_MAGENTA, // see note about trailing comma on the last enumerator below
}; // however the enum itself must end with a semicolon
 
// Define a few variables of enumerated type Color
Color paint = COLOR_WHITE;
Color house(COLOR_BLUE);
Color apple { COLOR_RED };

Định nghĩa một enum (hoặc bất kỳ loại dữ liệu do người dùng định nghĩa) sẽ không cấp phát bất kỳ bộ nhớ nào cả. Khi một biến của kiểu enum được khai báo (chẳng hạn như biến paint trong ví dụ trên), bộ nhớ chỉ được cấp phát cho biến đó tại thời điểm nó được khái báo.

Lưu ý rằng mỗi thành viên của enum được phân tách bằng dấu phẩy và toàn bộ enum được kết thúc bằng dấu chấm phẩy.

Trước C ++ 11, dấu phẩy sau enum cuối cùng (ví dụ: sau COLOR_MAGENTA) không được phép (mặc dù nhiều trình biên dịch vẫn chấp nhận nó). Tuy nhiên, bắt đầu với C ++ 11, dấu phẩy được cho phép. Bây giờ trình biên dịch C ++ 11 phổ biến hơn, sử dụng dấu phẩy sau phần tử cuối cùng thường được coi là chấp nhận được.

1. Tên của enum và thành viên của nó

Cung cấp một tên cho một enum là tùy ý. Enums không có tên đôi khi được gọi là enums nặc danh. Tên enum thường được đặt tên bắt đầu bằng một chữ in hoa.

Các thành viên của enum phải được đặt tên và thường được đặt tên bằng cách sử dụng tất cả các chữ hoa (ví dụ: COLOR_WHITE) hoặc có tiền tố k và xen kẽ (ví dụ: kColorWhite).

2. Phạm vi của enum

Vì các thành viên của enum được đặt vào cùng một không gian tên khi định nghĩa một enum, nên một tên của thành viên trong enum có thể được sử dụng trong nhiều enum khác nhau nhưng nó có thể gây ra lỗi:

/**
* 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/
*/

enum Color
{
 RED,
 BLUE, // BLUE is put into the global namespace
 GREEN
};
 
enum Feeling
{
 HAPPY,
 TIRED,
 BLUE // error, BLUE was already used in enum Color in the global namespace
};

Do đó, nó tên của các thành viên của enum thường có các tiền tố như ANIMAL_ hoặc COLOR_, cả hai cái này để ngăn xung đột đặt tên và giúp làm rõ mục đích sử dụng.

3. Các giá trị của enum

Mỗi điều tra viên được tự động gán một giá trị số nguyên dựa trên vị trí của nó trong danh sách enum. Theo mặc định, phần tử enum đầu tiên được gán giá trị số nguyên 0 và mỗi phần tử tiếp theo có giá trị lớn hơn phần tử trước đó:

enum Color
{
    COLOR_BLACK, // assigned 0
    COLOR_RED, // assigned 1
    COLOR_BLUE, // assigned 2
    COLOR_GREEN, // assigned 3
    COLOR_WHITE, // assigned 4
    COLOR_CYAN, // assigned 5
    COLOR_YELLOW, // assigned 6
    COLOR_MAGENTA // assigned 7
};
 
Color paint(COLOR_WHITE);
std::cout << paint;

Câu lệnh cout ở trên in giá trị 4.

Có thể xác định rõ ràng giá trị của các phần tử enum. Các giá trị nguyên này có thể dương hoặc âm và có thể chia sẻ cùng giá trị với các phần tử khác. Bất kỳ phần tử nào không xác định sẽ được cung cấp một giá trị lớn hơn phần tử trước đó.

/**
* 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/
*/

// define a new enum named Animal
enum Animal
{
    ANIMAL_CAT = -3,
    ANIMAL_DOG, // assigned -2
    ANIMAL_PIG, // assigned -1
    ANIMAL_HORSE = 5,
    ANIMAL_GIRAFFE = 5, // shares same value as ANIMAL_HORSE
    ANIMAL_CHICKEN // assigned 6
};

Lưu ý trong trường hợp này, ANIMAL_HORSE và ANIMAL_GIRAFFE đã được đưa ra cùng một giá trị. Khi điều này xảy ra, các enum trở nên không khác biệt – về cơ bản, ANIMAL_HORSE và ANIMAL_GIRAFFE có thể thay thế cho nhau. Mặc dù C ++ cho phép, nhưng việc gán cùng một giá trị cho hai phần tử trong cùng một enum nên tránh.

Cách thực hành tốt nhất: Không nên gán giá trị cụ thể cho các phần tử của bạn.
Quy tắc: Không nên gán cùng một giá trị cho hai phần tử trong cùng một enum trừ khi có một lý do rất chính đáng.

4. Đánh giá gía trị Enum và input/output

Vì các giá trị của enum được đánh giá là các số nguyên, chúng có thể được gán cho các biến số nguyên. Điều này có nghĩa là chúng cũng có thể là đầu ra (dưới dạng số nguyên), vì std :: cout để biết cách xuất số nguyên.

int mypet = ANIMAL_PIG;
std::cout << ANIMAL_HORSE; // evaluates to integer before being passed to std::cout

Điều này tạo ra kết quả: 5

Trình biên dịch sẽ không hoàn toàn chuyển đổi một số nguyên thành một giá trị enum. Sau đây sẽ tạo ra một lỗi biên dịch:

Animal animal = 5; // will cause compiler error

Tuy nhiên, bạn có thể buộc nó làm như vậy thông qua static_cast:

Color color = static_cast<Color>(5); // ugly

Trình biên dịch cũng sẽ không cho phép bạn nhập enum bằng std :: cin:

/**
* 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/
*/

enum Color
{
    COLOR_BLACK, // assigned 0
    COLOR_RED, // assigned 1
    COLOR_BLUE, // assigned 2
    COLOR_GREEN, // assigned 3
    COLOR_WHITE, // assigned 4
    COLOR_CYAN, // assigned 5
    COLOR_YELLOW, // assigned 6
    COLOR_MAGENTA // assigned 7
};
 
Color color;
std::cin >> color; // will cause compiler error

Một cách giải quyết là đọc số nguyên và sử dụng static_cast để buộc trình biên dịch đặt giá trị số nguyên thành kiểu enum:

int inputColor;
std::cin >> inputColor;
 
Color color{ static_cast<Color>(inputColor) };

Mỗi loại enum được coi là một loại riêng biệt. Do đó, cố gắng gán các phần tử từ loại enum này sang loại enum khác sẽ gây ra lỗi biên dịch:

Animal animal{ COLOR_BLUE }; // will cause compiler error

Nếu bạn muốn sử dụng một kiểu số nguyên khác cho các phần tử enum, ví dụ để tiết kiệm băng thông khi nối mạng một phần tử, bạn có thể chỉ định nó tại khai báo enum.

// Use an 8 bit unsigned integer as the enum base.
enum Color : std::uint_least8_t
{
    COLOR_BLACK,
    COLOR_RED,
    // ...
};

Vì các phần tử trong enum không thường được sử dụng cho số học hoặc so sánh, nên nó an toàn khi sử dụng một số nguyên không dấu. Chúng ta cũng cần xác định cơ sở enum khi chúng ta muốn chuyển tiếp khai báo một enum.

enum Color; // Error
enum Color : int; // Okay
 
// ...
 
// Because Color was forward declared with a fixed base, we
// need to specify the base again at the definition.
enum Color : int
{
    COLOR_BLACK,
    COLOR_RED,
    // ...
};

Cũng như với các biến không đổi, các kiểu enum hiển thị trong trình gỡ lỗi, làm cho chúng hữu ích hơn các giá trị #00001.

5. Printing enum

Như bạn đã thấy ở trên, cố gắng in một giá trị enum bằng cách sử dụng std :: cout dẫn đến giá trị nguyên của enum được in. Vậy làm thế nào bạn có thể in chính phần tử dưới dạng văn bản? Một cách để làm như vậy là viết hàm và sử dụng câu lệnh if:

/**
* 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/
*/

enum Color
{
    COLOR_BLACK, // assigned 0
    COLOR_RED, // assigned 1
    COLOR_BLUE, // assigned 2
    COLOR_GREEN, // assigned 3
    COLOR_WHITE, // assigned 4
    COLOR_CYAN, // assigned 5
    COLOR_YELLOW, // assigned 6
    COLOR_MAGENTA // assigned 7
};
 
void printColor(Color color)
{
    if (color == COLOR_BLACK)
        std::cout << "Black";
    else if (color == COLOR_RED)
        std::cout << "Red";
    else if (color == COLOR_BLUE)
        std::cout << "Blue";
    else if (color == COLOR_GREEN)
        std::cout << "Green";
    else if (color == COLOR_WHITE)
        std::cout << "White";
    else if (color == COLOR_CYAN)
        std::cout << "Cyan";
    else if (color == COLOR_YELLOW)
        std::cout << "Yellow";
    else if (color == COLOR_MAGENTA)
        std::cout << "Magenta";
    else
        std::cout << "Who knows!";
}

Khi bạn đã học cách sử dụng các câu lệnh switch, bạn có thể muốn sử dụng các câu lệnh đó thay vì một loạt các câu lệnh if / else, vì nó giúp dễ đọc hơn.

6. Cấp phát bộ nhớ cho Enum

Các loại Enum được coi là một phần của họ các kiểu số nguyên và nó dựa vào trình biên dịch để xác định dung lượng bộ nhớ được cấp phát cho một biến enum. C++ cho biết kích thước enum cần đủ lớn để thể hiện tất cả các giá trị enum. Thông thường, nó sẽ làm cho các biến enum có cùng kích thước với một int chuẩn.

7. Các phần tử trong enum hữu ích cho việc gì?

Các kiểu enum cực kỳ hữu ích cho code và mục đích dễ đọc khi bạn cần biểu diễn một tập hợp các trạng thái cụ thể, được xác định trước.

Ví dụ, các hàm thường trả về số nguyên cho người gọi để biểu diễn mã lỗi khi có lỗi xảy ra bên trong hàm. Thông thường, các số âm nhỏ được sử dụng để thể hiện các mã lỗi khác nhau có thể. Ví dụ:

/**
* 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/
*/

int readFileContents()
{
    if (!openFile())
        return -1;
    if (!readFile())
        return -2;
    if (!parseFile())
        return -3;
 
    return 0; // success
}

Tuy nhiên, sử dụng những con số ma thuật như thế này là rất rối. Một phương pháp thay thế sẽ thông qua việc sử dụng một kiểu enum:

/**
* 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/
*/

enum ParseResult
{
    SUCCESS = 0,
    ERROR_OPENING_FILE = -1,
    ERROR_READING_FILE = -2,
    ERROR_PARSING_FILE = -3
};
 
ParseResult readFileContents()
{
    if (!openFile())
        return ERROR_OPENING_FILE;
    if (!readFile())
        return ERROR_READING_FILE;
    if (!parsefile())
        return ERROR_PARSING_FILE;
 
    return SUCCESS;
}

Điều này dễ đọc và dễ hiểu hơn nhiều so với việc sử dụng các giá trị trả về số ma thuật. Hơn nữa, người gọi có thể kiểm tra giá trị trả về của hàm đối với hàm enum thích hợp, dễ hiểu hơn so với kiểm tra kết quả trả về cho một giá trị nguyên cụ thể.

if (readFileContents() == SUCCESS)
    {
    // do something
    }
else
    {
    // print error message
    }

Các kiểu enum được sử dụng tốt nhất khi xác định một bộ định danh liên quan. Ví dụ: giả sử bạn nói rằng bạn đang viết một trò chơi trong đó người chơi có thể mang một vật phẩm, nhưng vật phẩm đó có thể là một số loại khác nhau. Bạn có thể làm điều này:

/**
* 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>
#include <string>
 
enum ItemType
{
    ITEMTYPE_SWORD,
    ITEMTYPE_TORCH,
    ITEMTYPE_POTION
};
 
std::string getItemName(ItemType itemType)
{
    if (itemType == ITEMTYPE_SWORD)
        return std::string("Sword");
    if (itemType == ITEMTYPE_TORCH)
        return std::string("Torch");
    if (itemType == ITEMTYPE_POTION)
        return std::string("Potion");
 
    // Just in case we add a new item in the future and forget to update this function
    return std::string("???");
}
 
int main()
{
    // ItemType is the enumerated type we've defined above.
    // itemType (lower case i) is the name of the variable we're defining (of type ItemType).
    // ITEMTYPE_TORCH is the enumerated value we're initializing variable itemType with.
    ItemType itemType = ITEMTYPE_TORCH;
 
    std::cout << "You are carrying a " << getItemName(itemType) << "\n";
 
    return 0;
}

Hoặc cách khác, nếu bạn đang viết một hàm để sắp xếp một loạt các giá trị:

enum SortType
{
    SORTTYPE_FORWARD,
    SORTTYPE_BACKWARDS
};
 
void sortData(SortType type)
{
    if (type == SORTTYPE_FORWARD)
        // sort data in forward order
    else if (type == SORTTYPE_BACKWARDS)
        // sort data in backwards order
}

Nhiều ngôn ngữ sử dụng enum để định nghĩa boolean. Một boolean về cơ bản chỉ là một enum với 2 điều tra viên: sai và đúng! Tuy nhiên, trong C ++, true và false được định nghĩa là từ khóa thay vì enum.

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