Enum vs Strong type enum


84

Tôi là người mới bắt đầu lập trình C ++.

Hôm nay tôi bắt gặp một chủ đề mới: được đánh máy mạnh mẽ enum. Tôi đã nghiên cứu về nó một chút nhưng cho đến giờ tôi vẫn không thể tìm ra lý do tại sao chúng ta cần cái này và công dụng của cái này là gì?

Ví dụ nếu chúng ta có:

enum xyz{a, b, c};
/*a = 0, b = 1, c = 2, (Typical C format)*/

Tại sao chúng ta cần viết:

enum class xyz{a, b, c};

Chúng ta đang cố gắng làm gì ở đây? Nghi ngờ quan trọng nhất của tôi là làm thế nào để sử dụng nó. Bạn có thể cung cấp một ví dụ nhỏ để tôi hiểu.

Câu trả lời:


114

OK, ví dụ đầu tiên: enums kiểu cũ không có phạm vi riêng:

enum Animals {Bear, Cat, Chicken};
enum Birds {Eagle, Duck, Chicken}; // error! Chicken has already been declared!

enum class Fruits { Apple, Pear, Orange };
enum class Colours { Blue, White, Orange }; // no problem!

Thứ hai, chúng chuyển đổi ngầm sang các kiểu tích phân, có thể dẫn đến hành vi kỳ lạ:

bool b = Bear && Duck; // what?

Cuối cùng, bạn có thể chỉ định kiểu tích phân cơ bản của enums C ++ 11:

enum class Foo : char { A, B, C};

Trước đây, loại cơ bản không được chỉ định, điều này có thể gây ra sự cố tương thích giữa các nền tảng. Chỉnh sửa Nó đã được chỉ ra trong các nhận xét rằng bạn cũng có thể chỉ định kiểu tích phân cơ bản của một enum "kiểu cũ" trong C ++ 11.


Chúng ta có cần khai báo / định nghĩa enum class Coloursenum class Fruits. Bởi vì khi tôi viết mã trong VS 2010. Nó ném một lỗi "expects a defination or a tag name"bên dưới class.
Rasmi Ranjan Nayak

Ngoài ra: Đối với enum "bình thường" trong C ++ 11 như trong C ++ 98 mặc định loại cơ bản không được định nghĩa
bruziuz

2
Ngoài ra: Bạn có thể khai báo chuyển tiếp enum-s, nếu loại cơ bản được chỉ định. Đối với enum thông thường trong C ++ 11, C ++ 98 thì không được phép. Trình biên dịch của Microsoft cho phép bạn thực hiện khai báo phía trước của enum, nhưng nó chỉ là phần mở rộng của MS, nó không phải là standart (ví dụ: gcc không cho phép nó) Vì vậy, điều đó bây giờ là hợp pháp: enum ForwardDeclare: std :: uint8_t;
bruziuz

Chúng ta có thể có các enum có phạm vi cũng chuyển đổi ngầm thành một kiểu tích phân không?
SS Anne

17

Có một bài viết hay về enums tại trang IBM này , nó rất chi tiết và được viết tốt. Tóm lại, đây là một số điểm quan trọng:

Các enums có phạm vi giải quyết hầu hết các hạn chế do enums thông thường: an toàn kiểu hoàn chỉnh, kiểu cơ bản được xác định rõ ràng, các vấn đề về phạm vi và khai báo chuyển tiếp.

  • Bạn có được sự an toàn về loại bằng cách không cho phép tất cả các chuyển đổi ngầm của enums có phạm vi sang các loại khác.
  • Bạn nhận được một phạm vi mới và enum không còn nằm trong phạm vi bao quanh nữa, tự cứu nó khỏi xung đột tên.
  • Các enum có phạm vi cung cấp cho bạn khả năng chỉ định kiểu cơ bản của kiểu liệt kê và đối với các enum có phạm vi, nó mặc định là int nếu bạn chọn không chỉ định nó.
  • Bất kỳ enum nào có kiểu cơ bản cố định đều có thể được khai báo phía trước.

2
Điểm thứ ba và thứ tư không dành riêng cho việc liệt kê theo phạm vi; bạn có thể chỉ định kiểu cơ bản của bất kỳ kiểu liệt kê nào.
Mike Seymour

1
Có ai có liên kết đến phiên bản PDF ít bị hỏng hơn không? Các ví dụ mã trong đó không hiển thị trong bất kỳ trình xem PDF nào của tôi, điều này để lại rất nhiều cho trí tưởng tượng.
Sara Sinback

11

Giá trị của enum classthực sự thuộc loại enum class, không phải underlying_typenhư đối với C-enums.

enum xyz { a, b, c};
enum class xyz_c { d, f, e };

void f(xyz x)
{
}

void f_c(xyz_c x)
{
}

// OK.
f(0);
// OK for C++03 and C++11.
f(a);
// OK with C++11.
f(xyz::a);
// ERROR.
f_c(0);
// OK.
f_c(xyz_c::d);

5

Các lớp enum ("enum mới", "enum mạnh") giải quyết ba vấn đề với kiểu liệt kê C ++ truyền thống:

  1. enumschuyển đổi ngầm quy ước sang int, gây ra lỗi khi ai đó không muốn một kiểu liệt kê hoạt động như một số nguyên.
  2. thông thường enumsxuất điều tra viên của họ ra phạm vi xung quanh, gây ra xung đột tên.
  3. Loại cơ bản của một enumkhông thể được chỉ định, gây ra sự nhầm lẫn, các vấn đề tương thích và làm cho việc khai báo chuyển tiếp không thể thực hiện được.

enum class ("enums mạnh") được gõ mạnh và có phạm vi:

enum Alert { green, yellow, orange, red }; // traditional enum

enum class Color { red, blue };   // scoped and strongly typed enum
                                  // no export of enumerator names into enclosing scope
                                  // no implicit conversion to int
enum class TrafficLight { red, yellow, green };

Alert a = 7;              // error (as ever in C++)
Color c = 7;              // error: no int->Color conversion

int a2 = red;             // ok: Alert->int conversion
int a3 = Alert::red;      // error in C++98; ok in C++11
int a4 = blue;            // error: blue not in scope
int a5 = Color::blue;     // error: not Color->int conversion

Color a6 = Color::blue;   // ok

Như được hiển thị, enum truyền thống hoạt động như bình thường, nhưng bây giờ bạn có thể tùy chọn đủ điều kiện với tên của enum.

Các enum mới là "enum class" vì chúng kết hợp các khía cạnh của kiểu liệt kê truyền thống (giá trị tên) với các khía cạnh của các lớp (các thành viên có phạm vi và không có chuyển đổi).

Có thể chỉ định kiểu cơ bản cho phép khả năng tương tác đơn giản hơn và kích thước được đảm bảo của bảng liệt kê:

enum class Color : char { red, blue };  // compact representation

enum class TrafficLight { red, yellow, green };  // by default, the underlying type is int

enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U };   // how big is an E?
                                                 // (whatever the old rules say;
                                                 // i.e. "implementation defined")

enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U };   // now we can be specific

Nó cũng cho phép khai báo phía trước các enums:

enum class Color_code : char;     // (forward) declaration
void foobar(Color_code* p);       // use of forward declaration
// ...
enum class Color_code : char { red, yellow, green, blue }; // definition

Kiểu cơ bản phải là một trong các kiểu số nguyên có dấu hoặc không dấu; mặc định là int.

Trong thư viện chuẩn, enumcác lớp được sử dụng cho:

  1. Hệ thống bản đồ mã lỗi cụ thể: Trong <system_error>: enum class errc;
  2. Các chỉ số an toàn của con trỏ: Trong <memory>:enum class pointer_safety { relaxed, preferred, strict };
  3. Lỗi luồng I / O: Trong <iosfwd>:enum class io_errc { stream = 1 };
  4. Xử lý lỗi truyền thông không đồng bộ: Trong <future>:enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

Một số trong số này có các toán tử, chẳng hạn như ==được định nghĩa.


3

Phạm vi Enum

Các bảng kê xuất các bảng kê của họ sang phạm vi xung quanh. Điều này có hai hạn chế. Đầu tiên, nó có thể dẫn đến xung đột tên, nếu hai điều tra viên ở các vùng khác nhau được khai báo trong cùng một phạm vi có cùng tên; thứ hai, không thể sử dụng một điều tra viên có tên đủ điều kiện, bao gồm cả tên enum.

enum ESet {a0, a, a1, b1, c3};
enum EAlpha{a, b, c}

select = ESet::a; // error
select = a;       // is ambigious
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.