Untuk Apa enum
Struct mengelompokkan beberapa nilai yang saling terkait ke dalam satu objek. enum menyelesaikan masalah yang berbeda: ia memberi nama pada sekumpulan kecil pilihan yang tetap. Daripada mengingat bahwa 0 berarti "merah", 1 berarti "hijau", dan 2 berarti "biru", kamu menulis Color::Red dan compiler menjagamu tetap konsisten.
Memakai enum setiap kali sebuah variabel hanya bisa berupa salah satu dari segelintir keadaan bernama, seperti warna lampu lalu lintas, jenis kartu, atau status koneksi, membuat kode menjelaskan dirinya sendiri dan membiarkan compiler menangkap salah ketik serta kasus yang terlewat, hal yang tidak akan pernah dilakukan bilangan bulat biasa.
Mendeklarasikan enum
C++ modern punya dua jenis. Mulailah dengan jenis yang hampir selalu sebaiknya kamu pakai: enum class yang ber-scope. Kamu menuliskan nama-namanya, dan setiap enumerator diakses lewat nama enum dengan :::
Perhatikan bahwa kamu menulis Color::Green, tidak pernah Green saja. Nilai-nilainya sendiri hanyalah label: kamu membandingkan, mengisi, dan meneruskannya, tetapi jarang peduli dengan angka di baliknya. Secara default Red bernilai 0, Green bernilai 1, dan Blue bernilai 2, dihitung naik mulai dari nol.
enum Biasa vs enum class
enum lama yang tidak ber-scope (tanpa kata kunci class) langsung menaruh nama-namanya ke scope di sekitarnya dan dikonversi ke int dengan sendirinya. Kedengarannya praktis, tetapi ini menimbulkan dua masalah nyata:
enum Color { Red, Green, Blue };
enum Fruit { Apple, Banana, Red }; // error: 'Red' already declared
enum Status { Active, Inactive };
int x = Active; // compiles silently - is this what you meant?
if (Active == Banana) { // compares unrelated enums via int - allowed!
}
Karena enumerator biasa adalah nama global, dua enum bisa bertabrakan hanya karena memakai label yang sama. Dan karena keduanya berubah menjadi int, compiler dengan senang hati membandingkan nilai dari enum yang sama sekali tidak berhubungan. enum class yang ber-scope memperbaiki keduanya: nama-namanya tinggal di dalam tipe, dan tipe itu tidak akan diam-diam berubah menjadi int:
Patokannya: pakai enum class sebagai default. Kembali ke enum biasa hanya kalau kamu memang menginginkan konversi implisit ke int, misalnya untuk konstanta flag gaya C yang lama.
Nilai Sendiri dan Underlying Type
Kamu bisa memberi angka eksplisit pada enumerator. Yang tidak kamu beri nilai akan melanjutkan hitungan dari nilai sebelumnya, yang berguna untuk hal seperti kode status HTTP atau bit flag:
Setiap enum didukung oleh sebuah tipe bilangan bulat, secara default int. Kamu bisa menetapkannya ke tipe yang lebih kecil kalau ukuran penting bagimu, misalnya saat menyimpan banyak enum di struktur yang padat atau saat harus cocok dengan format data tertentu:
Memilih underlying type juga menjamin rentang yang harus dipenuhi nilai-nilaimu: enum uint8_t tidak bisa menyimpan nilai di atas 255.
Konversi antara enum dan int
enum class yang ber-scope tidak pernah dikonversi secara implisit, dan memang itulah intinya. Saat kamu benar-benar butuh angkanya, misalnya untuk mencetaknya, menjadikannya indeks array, atau membacanya dari file, pakai static_cast. Konversi dari enum ke int selalu aman:
Mengonversi int kembali ke enum adalah arah yang berbahaya. Cast tidak mengecek bahwa angkanya sesuai dengan enumerator yang benar-benar ada; ia akan memberimu nilai yang secara teknis berada di luar kumpulan nama enum itu:
Suit s = static_cast<Suit>(2); // fine - that's Clubs
Suit bad = static_cast<Suit>(99); // compiles, but 99 is not a valid Suit
// using `bad` in a switch or as an array index is a lurking bug
Kalau bilangan bulatnya berasal dari input pengguna atau file, validasi rentangnya sendiri sebelum melakukan cast. Kalau tidak, kamu akan menciptakan nilai yang tidak pernah ditangani case mana pun, sumber halus undefined behavior di kemudian hari.
Memakai enum dengan switch
Karena enum berarti "salah satu dari kumpulan tetap", ia sangat cocok dipasangkan dengan switch. Kalau kamu menangani setiap enumerator, banyak compiler akan memperingatkanmu saat nanti kamu menambah nilai baru dan lupa menanganinya. Ini keamanan gratis yang tidak kamu dapat dari bilangan bulat mentah:
Satu jebakan: tidak ada cara bawaan untuk mencetak nama sebuah enumerator. cout << TrafficLight::Red tidak akan bisa dikompilasi untuk enum yang ber-scope, dan bahkan untuk enum biasa yang dicetak adalah angkanya, bukan "Red". switch kecil atau tabel pencarian seperti contoh di atas adalah cara yang biasa dipakai untuk mengubah enum menjadi string yang bisa dibaca manusia.
Berikutnya: Exception
Enum dan struct membantumu memodelkan seperti apa datamu. Namun program sungguhan juga harus menghadapi saat-saat ketika sesuatu salah: file yang tidak mau terbuka, angka yang tidak bisa di-parse, nilai yang di luar rentang. C++ menangani jalur kegagalan seperti itu dengan exception, dan itulah topik halaman berikutnya.
Pertanyaan yang sering diajukan
Apa perbedaan enum dan enum class di C++?
enum biasa membocorkan nama-namanya ke scope di sekitarnya dan otomatis dikonversi ke int, sehingga menimbulkan bentrokan nama dan perbandingan yang tidak disengaja. enum class yang ber-scope menyimpan nama-namanya di dalam enum (Color::Red) dan menolak dikonversi ke int tanpa cast eksplisit. Utamakan enum class di C++ modern, karena ia type-safe dan menghindari jebakan klasik.
Bagaimana cara mengonversi enum C++ ke int?
enum biasa dikonversi secara implisit, jadi int n = Red; langsung berfungsi. enum class yang ber-scope butuh cast eksplisit: int n = static_cast<int>(Color::Red);. Untuk arah sebaliknya, cast int itu kembali: Color c = static_cast<Color>(2);. Tetapi hati-hati, saat program berjalan tidak ada pengecekan bahwa nilai itu adalah enumerator yang valid.
Enum C++ pertama dimulai dari nilai berapa?
Secara default enumerator pertama bernilai 0, dan setiap enumerator sesudahnya satu lebih besar dari sebelumnya. Jadi di enum class Level { Low, Mid, High };, Low bernilai 0, Mid bernilai 1, dan High bernilai 2. Kamu bisa memberi nilai eksplisit pada salah satunya untuk mengubah urutan ini.