Ketika seseorang mengklaim bahwa beberapa pemrograman pemrograman adalah "jahat" mereka mencoba untuk mencegah Anda berpikir sendiri.
Pete Becker
3
@NicolBolas: Ini lebih merupakan pertanyaan retoris untuk memberikan jawaban FAQ (apakah ini benar-benar sering ditanyakan adalah cerita yang berbeda).
David Rodríguez - dribeas
@ David, ada diskusi apakah ini harus menjadi FAQ atau tidak yang dimulai di sini . Masukan sambutan.
sbi
17
@PeteBecker Terkadang mereka hanya berusaha melindungi Anda dari diri sendiri.
piccy
geeksforgeeks.org/... ini juga merupakan tempat yang baik untuk memahami enumvs enum class.
mr_azad
Jawaban:
473
C ++ memiliki dua jenis enum:
enum classes
enumS polos
Berikut adalah beberapa contoh cara mendeklarasikannya:
enumclassColor{ red, green, blue };// enum classenumAnimal{ dog, cat, bird, human };// plain enum
Apa perbedaan antara keduanya?
enum classnama es - enumerator adalah lokal untuk enum dan nilainya tidak secara implisit dikonversi ke tipe lain (seperti yang lain enumatau int)
Plain enums - di mana nama enumerator berada dalam cakupan yang sama dengan enum dan nilainya secara implisit dikonversi menjadi bilangan bulat dan tipe lainnya
Contoh:
enumColor{ red, green, blue };// plain enum enumCard{ red_card, green_card, yellow_card };// another plain enum enumclassAnimal{ dog, deer, cat, bird, human };// enum classenumclassMammal{ kangaroo, deer, human };// another enum classvoid fun(){// examples of bad use of plain enums:Color color =Color::red;Card card =Card::green_card;int num = color;// no problemif(color ==Card::red_card)// no problem (bad)
cout <<"bad"<< endl;if(card ==Color::green)// no problem (bad)
cout <<"bad"<< endl;// examples of good use of enum classes (safe)Animal a =Animal::deer;Mammal m =Mammal::deer;int num2 = a;// errorif(m == a)// error (good)
cout <<"bad"<< endl;if(a ==Mammal::deer)// error (good)
cout <<"bad"<< endl;}
Kesimpulan:
enum classHarus lebih disukai karena mereka menyebabkan lebih sedikit kejutan yang berpotensi menyebabkan bug.
Contoh yang bagus ... apakah ada cara untuk menggabungkan jenis keamanan dari versi kelas dengan promosi namespace dari versi enum? Yaitu, jika saya memiliki kelas Adengan negara, dan saya membuat enum class State { online, offline };sebagai anak kelas A, saya ingin melakukan state == onlinepemeriksaan di dalam Adaripada state == State::online... apakah itu mungkin?
tandai
31
Nggak. Promosi namespace adalah Bad Thing ™ dan setengah dari justifikasi enum classadalah untuk menghilangkannya.
Puppy
10
Di C ++ 11, Anda dapat menggunakan enum yang diketik secara eksplisit juga, seperti enum Animal: unsigned int {dog, deer, cat, bird}
Blasius Secundus
3
@Cat Plus Plus Saya mengerti bahwa @Oleksiy mengatakan itu buruk. Pertanyaan saya bukanlah apakah Oleksiy menganggapnya buruk. Pertanyaan saya adalah permintaan untuk menjelaskan apa yang buruk tentangnya. Secara khusus, mengapa Oleksiy, misalnya, menganggap buruk Color color = Color::red.
chux - Reinstate Monica
9
@Cat Plus Plus Jadi contoh buruk tidak terjadi sampai if (color == Card::red_card)baris, 4 baris lebih lambat dari komentar (yang saya lihat sekarang berlaku untuk paruh pertama blok.) 2 baris blok memberikan contoh buruk . 3 baris pertama tidak masalah. "Seluruh blok adalah mengapa enums polos buruk" melemparkan saya karena saya pikir Anda berarti ada yang salah dengan itu juga. Saya mengerti sekarang, itu hanya set-up. Bagaimanapun, terima kasih atas umpan baliknya.
The enum classes ( "enum baru", "enum kuat") alamat tiga masalah dengan tradisional C ++ mantri:
enum konvensional secara implisit dikonversi ke int, menyebabkan kesalahan ketika seseorang tidak ingin enumerasi bertindak sebagai integer.
Enum konvensional mengekspor enumerator mereka ke ruang lingkup di sekitarnya, menyebabkan bentrokan nama.
tipe yang mendasari suatu enumtidak dapat ditentukan, menyebabkan kebingungan, masalah kompatibilitas, dan membuat pernyataan maju tidak mungkin.
Enum baru adalah "kelas enum" karena mereka menggabungkan aspek-aspek enumerasi tradisional (nilai-nilai nama) dengan aspek-aspek kelas (anggota lingkup dan tidak adanya konversi).
Jadi, seperti yang disebutkan oleh pengguna lain, "enum yang kuat" akan membuat kode lebih aman.
Tipe yang mendasari "klasik" enumharus merupakan tipe integer yang cukup besar untuk memenuhi semua nilai dari enum; ini biasanya sebuah int. Juga setiap tipe yang disebutkan harus kompatibel dengan charatau tipe integer yang ditandatangani / tidak ditandatangani.
Ini adalah deskripsi luas tentang apa yang enumharus menjadi tipe yang mendasarinya, sehingga setiap kompiler akan mengambil keputusan sendiri tentang tipe yang mendasarinya enumdan kadang-kadang hasilnya bisa mengejutkan.
Sebagai contoh, saya telah melihat kode seperti ini beberapa kali:
enum E_MY_FAVOURITE_FRUITS
{
E_APPLE =0x01,
E_WATERMELON =0x02,
E_COCONUT =0x04,
E_STRAWBERRY =0x08,
E_CHERRY =0x10,
E_PINEAPPLE =0x20,
E_BANANA =0x40,
E_MANGO =0x80,
E_MY_FAVOURITE_FRUITS_FORCE8 =0xFF// 'Force' 8bits, how can you tell?};
Dalam kode di atas, beberapa pembuat kode naif berpikir bahwa kompiler akan menyimpan E_MY_FAVOURITE_FRUITSnilai - nilai ke dalam tipe 8bit yang tidak ditandatangani ... tetapi tidak ada garansi tentang hal itu: kompiler dapat memilih unsigned charatau intatau short, salah satu dari jenis itu cukup besar untuk memenuhi semua nilai yang terlihat di enum. Menambahkan bidang E_MY_FAVOURITE_FRUITS_FORCE8adalah beban dan tidak memaksa kompiler untuk membuat pilihan apa pun tentang tipe yang mendasarinya enum.
Jika ada beberapa bagian dari kode yang bergantung pada ukuran tipe dan / atau mengasumsikan bahwa E_MY_FAVOURITE_FRUITSakan menjadi lebar (misalnya: rutinisasi serialisasi) kode ini dapat berperilaku dalam beberapa cara aneh tergantung pada pemikiran kompiler.
Dan untuk membuat keadaan menjadi lebih buruk, jika beberapa rekan kerja secara sembrono menambahkan nilai baru pada kita enum:
E_DEVIL_FRUIT =0x100,// New fruit, with value greater than 8bits
Kompiler tidak mengeluh tentang itu! Itu hanya mengubah ukuran jenis agar sesuai dengan semua nilai enum(dengan asumsi bahwa kompiler menggunakan jenis terkecil mungkin, yang merupakan asumsi yang tidak bisa kita lakukan). Penambahan sederhana dan ceroboh ini enumdapat secara halus memecahkan kode terkait.
Karena C ++ 11 dimungkinkan untuk menentukan tipe yang mendasarinya untuk enumdan enum class(terima kasih rdb ) sehingga masalah ini diatasi dengan rapi:
Menentukan tipe yang mendasarinya jika bidang memiliki ekspresi di luar rentang tipe ini kompiler akan mengeluh bukannya mengubah tipe yang mendasarinya.
Saya pikir ini adalah peningkatan keamanan yang baik.
Jadi, mengapa kelas enum lebih disukai daripada enum biasa? , jika kita dapat memilih tipe yang mendasari untuk scoped ( enum class) dan unscoped ( enum) enum apa lagi yang membuat enum classpilihan yang lebih baik ?:
Saya kira kita dapat membatasi tipe basis enum untuk enum reguler juga, selama kita memiliki C ++ 11
Sagar Padhye
11
Maaf, tapi jawaban ini salah. "enum class" tidak ada hubungannya dengan kemampuan untuk menentukan jenisnya. Itu fitur independen yang ada untuk enum reguler dan untuk kelas enum.
rdb
14
Ini masalahnya: * Kelas Enum adalah fitur baru di C ++ 11. * Enums yang diketik adalah fitur baru di C ++ 11. Ini adalah dua fitur baru yang tidak terkait terpisah di C ++ 11. Anda dapat menggunakan keduanya, atau Anda bisa menggunakan keduanya, atau tidak keduanya.
rdb
2
Saya pikir Alex Allain memberikan penjelasan sederhana paling lengkap yang belum saya lihat di blog ini di [ cprogramming.com/c++11/… . Enum tradisional baik untuk menggunakan nama bukan nilai integer dan menghindari menggunakan preprocessor #defines, yang merupakan Good Thing - itu menambahkan kejelasan. enum class menghapus konsep nilai numerik enumerator, dan memperkenalkan lingkup dan pengetikan yang kuat yang meningkat (well, dapat meningkatkan :-) kebenaran program. Ini menggerakkan Anda selangkah lebih dekat ke pemikiran berorientasi objek.
Jon Spencer
2
Selain itu, selalu lucu ketika Anda meninjau kode dan tiba-tiba One Piece terjadi.
Justin Time - Pasang kembali Monica
47
Keuntungan dasar menggunakan kelas enum daripada enum normal adalah bahwa Anda mungkin memiliki variabel enum yang sama untuk 2 enum yang berbeda dan masih dapat menyelesaikannya (yang telah disebut sebagai tipe safe oleh OP)
Untuk misalnya:
enumclassColor1{ red, green, blue };//this will compileenumclassColor2{ red, green, blue };enumColor1{ red, green, blue };//this will not compile enumColor2{ red, green, blue };
Adapun enum dasar, kompiler tidak akan dapat membedakan apakah redmerujuk ke tipe Color1atau Color2seperti dalam hte di bawah pernyataan.
enumColor1{ red, green, blue };enumColor2{ red, green, blue };int x = red;//Compile time error(which red are you refering to??)
@Oleksiy Ohh saya tidak membaca pertanyaan Anda dengan benar. Pertimbangkan adalah sebagai tambahan bagi mereka yang tidak tahu.
Saksham
tidak apa-apa! Saya hampir lupa tentang ini
Oleksiy
tentu saja, Anda akan menulis enum { COLOR1_RED, COLOR1_GREE, COLOR1_BLUE }, dengan mudah menghindari masalah namespace. Argumen namespace adalah salah satu dari tiga yang disebutkan di sini yang tidak saya beli sama sekali.
Jo So
2
@ Jo So Solusi itu adalah solusi yang tidak perlu. Enum: enum Color1 { COLOR1_RED, COLOR1_GREEN, COLOR1_BLUE }sebanding dengan kelas Enum: enum class Color1 { RED, GREEN, BLUE }. Mengakses sama: COLOR1_REDvs Color1::RED, tetapi versi Enum mengharuskan Anda mengetik "COLOR1" di setiap nilai, yang memberikan lebih banyak ruang untuk kesalahan ketik, yang perilaku namespace dari kelas enum menghindari.
cdgraham
2
Silakan gunakan kritik yang membangun . Ketika saya mengatakan lebih banyak ruang untuk kesalahan ketik, maksud saya ketika Anda awalnya mendefinisikan nilai-nilai enum Color1, yang tidak bisa ditangkap oleh kompiler karena kemungkinan masih berupa nama 'valid'. Jika saya menulis RED, GREENdan seterusnya menggunakan kelas enum, maka tidak dapat menyelesaikan enum Bananakarena mengharuskan Anda menentukan Color1::REDuntuk mengakses nilai (argumen namespace). Masih ada waktu yang baik untuk digunakan enum, tetapi perilaku namespace dari suatu enum classseringkali bisa sangat bermanfaat.
cdgraham
20
Enumerasi digunakan untuk mewakili satu set nilai integer.
Kata classkunci setelah enummenentukan bahwa enumerasi diketik dengan kuat dan enumeratornya dicakup. Dengan cara ini enumkelas mencegah penyalahgunaan konstanta secara tidak sengaja.
enum konvensional secara implisit dikonversi ke int, menyebabkan kesalahan ketika seseorang tidak ingin enumerasi bertindak sebagai integer.
enum color
{Red,Green,Yellow};enumclassNewColor{Red_1,Green_1,Yellow_1};int main(){//! Implicit conversion is possibleint i =Red;//! Need enum class name followed by access specifier. Ex: NewColor::Red_1int j =Red_1;// error C2065: 'Red_1': undeclared identifier//! Implicit converison is not possible. Solution Ex: int k = (int)NewColor::Red_1;int k =NewColor::Red_1;// error C2440: 'initializing': cannot convert from 'NewColor' to 'int'return0;}
Enum konvensional mengekspor enumerator mereka ke ruang lingkup di sekitarnya, menyebabkan bentrokan nama.
// Header.henum vehicle
{Car,Bus,Bike,Autorickshow};enumFourWheeler{Car,// error C2365: 'Car': redefinition; previous definition was 'enumerator'SmallBus};enumclassEditor{
vim,
eclipes,VisualStudio};enumclassCppEditor{
eclipes,// No error of redefinitionsVisualStudio,// No error of redefinitionsQtCreator};
Jenis enum yang mendasarinya tidak dapat ditentukan, menyebabkan kebingungan, masalah kompatibilitas, dan membuat pernyataan maju menjadi tidak mungkin.
C ++ 11 memungkinkan enum "non-kelas" untuk diketik juga. Masalah polusi namespace, dll, masih ada. Lihatlah jawaban yang relevan yang sudah ada jauh sebelum ini ..
user2864740
7
jangan secara implisit mengkonversi ke int
dapat memilih jenis yang mendasari
ENUM namespace untuk menghindari polusi terjadi
Dibandingkan dengan kelas normal, dapat dinyatakan maju, tetapi tidak memiliki metode
Perlu dicatat, di atas semua jawaban lain ini, C ++ 20 memecahkan salah satu masalah yang enum classada: verbosity. Membayangkan hipotetis sebuah enum class, Color.
Karena, seperti yang dikatakan dalam jawaban lain, class enum tidak secara implisit dapat dikonversi ke int / bool, itu juga membantu menghindari kode buggy seperti:
enumMyEnum{Value1,Value2,};...if(var ==Value1||Value2)// Should be "var == Value2" no error/warning
Untuk melengkapi komentar saya sebelumnya, perhatikan bahwa gcc sekarang memiliki peringatan yang disebut -Wint-in-bool-context yang akan menangkap kesalahan semacam ini.
Arnaud
0
Satu hal yang belum disebutkan secara eksplisit - fitur lingkup memberi Anda opsi untuk memiliki nama yang sama untuk metode enum dan kelas. Contohnya:
classTest{public:// these call ProcessCommand() internallyvoidTakeSnapshot();voidRestoreSnapshot();private:enumclassCommand// wouldn't be possible without 'class'{TakeSnapshot,RestoreSnapshot};voidProcessCommand(Command cmd);// signal the other thread or whatever};
enum
vsenum class
.Jawaban:
C ++ memiliki dua jenis
enum
:enum class
esenum
S polosBerikut adalah beberapa contoh cara mendeklarasikannya:
Apa perbedaan antara keduanya?
enum class
nama es - enumerator adalah lokal untuk enum dan nilainya tidak secara implisit dikonversi ke tipe lain (seperti yang lainenum
atauint
)Plain
enum
s - di mana nama enumerator berada dalam cakupan yang sama dengan enum dan nilainya secara implisit dikonversi menjadi bilangan bulat dan tipe lainnyaContoh:
Kesimpulan:
enum class
Harus lebih disukai karena mereka menyebabkan lebih sedikit kejutan yang berpotensi menyebabkan bug.sumber
A
dengan negara, dan saya membuatenum class State { online, offline };
sebagai anak kelasA
, saya ingin melakukanstate == online
pemeriksaan di dalamA
daripadastate == State::online
... apakah itu mungkin?enum class
adalah untuk menghilangkannya.Color color = Color::red
.if (color == Card::red_card)
baris, 4 baris lebih lambat dari komentar (yang saya lihat sekarang berlaku untuk paruh pertama blok.) 2 baris blok memberikan contoh buruk . 3 baris pertama tidak masalah. "Seluruh blok adalah mengapa enums polos buruk" melemparkan saya karena saya pikir Anda berarti ada yang salah dengan itu juga. Saya mengerti sekarang, itu hanya set-up. Bagaimanapun, terima kasih atas umpan baliknya.Dari FAQ C ++ 11 Bjarne Stroustrup :
Jadi, seperti yang disebutkan oleh pengguna lain, "enum yang kuat" akan membuat kode lebih aman.
Tipe yang mendasari "klasik"
enum
harus merupakan tipe integer yang cukup besar untuk memenuhi semua nilai darienum
; ini biasanya sebuahint
. Juga setiap tipe yang disebutkan harus kompatibel denganchar
atau tipe integer yang ditandatangani / tidak ditandatangani.Ini adalah deskripsi luas tentang apa yang
enum
harus menjadi tipe yang mendasarinya, sehingga setiap kompiler akan mengambil keputusan sendiri tentang tipe yang mendasarinyaenum
dan kadang-kadang hasilnya bisa mengejutkan.Sebagai contoh, saya telah melihat kode seperti ini beberapa kali:
Dalam kode di atas, beberapa pembuat kode naif berpikir bahwa kompiler akan menyimpan
E_MY_FAVOURITE_FRUITS
nilai - nilai ke dalam tipe 8bit yang tidak ditandatangani ... tetapi tidak ada garansi tentang hal itu: kompiler dapat memilihunsigned char
atauint
ataushort
, salah satu dari jenis itu cukup besar untuk memenuhi semua nilai yang terlihat dienum
. Menambahkan bidangE_MY_FAVOURITE_FRUITS_FORCE8
adalah beban dan tidak memaksa kompiler untuk membuat pilihan apa pun tentang tipe yang mendasarinyaenum
.Jika ada beberapa bagian dari kode yang bergantung pada ukuran tipe dan / atau mengasumsikan bahwa
E_MY_FAVOURITE_FRUITS
akan menjadi lebar (misalnya: rutinisasi serialisasi) kode ini dapat berperilaku dalam beberapa cara aneh tergantung pada pemikiran kompiler.Dan untuk membuat keadaan menjadi lebih buruk, jika beberapa rekan kerja secara sembrono menambahkan nilai baru pada kita
enum
:Kompiler tidak mengeluh tentang itu! Itu hanya mengubah ukuran jenis agar sesuai dengan semua nilai
enum
(dengan asumsi bahwa kompiler menggunakan jenis terkecil mungkin, yang merupakan asumsi yang tidak bisa kita lakukan). Penambahan sederhana dan ceroboh inienum
dapat secara halus memecahkan kode terkait.Karena C ++ 11 dimungkinkan untuk menentukan tipe yang mendasarinya untuk
enum
danenum class
(terima kasih rdb ) sehingga masalah ini diatasi dengan rapi:Menentukan tipe yang mendasarinya jika bidang memiliki ekspresi di luar rentang tipe ini kompiler akan mengeluh bukannya mengubah tipe yang mendasarinya.
Saya pikir ini adalah peningkatan keamanan yang baik.
Jadi, mengapa kelas enum lebih disukai daripada enum biasa? , jika kita dapat memilih tipe yang mendasari untuk scoped (
enum class
) dan unscoped (enum
) enum apa lagi yang membuatenum class
pilihan yang lebih baik ?:int
.sumber
Keuntungan dasar menggunakan kelas enum daripada enum normal adalah bahwa Anda mungkin memiliki variabel enum yang sama untuk 2 enum yang berbeda dan masih dapat menyelesaikannya (yang telah disebut sebagai tipe safe oleh OP)
Untuk misalnya:
Adapun enum dasar, kompiler tidak akan dapat membedakan apakah
red
merujuk ke tipeColor1
atauColor2
seperti dalam hte di bawah pernyataan.sumber
enum { COLOR1_RED, COLOR1_GREE, COLOR1_BLUE }
, dengan mudah menghindari masalah namespace. Argumen namespace adalah salah satu dari tiga yang disebutkan di sini yang tidak saya beli sama sekali.enum Color1 { COLOR1_RED, COLOR1_GREEN, COLOR1_BLUE }
sebanding dengan kelas Enum:enum class Color1 { RED, GREEN, BLUE }
. Mengakses sama:COLOR1_RED
vsColor1::RED
, tetapi versi Enum mengharuskan Anda mengetik "COLOR1" di setiap nilai, yang memberikan lebih banyak ruang untuk kesalahan ketik, yang perilaku namespace dari kelas enum menghindari.enum Color1
, yang tidak bisa ditangkap oleh kompiler karena kemungkinan masih berupa nama 'valid'. Jika saya menulisRED
,GREEN
dan seterusnya menggunakan kelas enum, maka tidak dapat menyelesaikanenum Banana
karena mengharuskan Anda menentukanColor1::RED
untuk mengakses nilai (argumen namespace). Masih ada waktu yang baik untuk digunakanenum
, tetapi perilaku namespace dari suatuenum class
seringkali bisa sangat bermanfaat.Enumerasi digunakan untuk mewakili satu set nilai integer.
Kata
class
kunci setelahenum
menentukan bahwa enumerasi diketik dengan kuat dan enumeratornya dicakup. Dengan cara inienum
kelas mencegah penyalahgunaan konstanta secara tidak sengaja.Sebagai contoh:
Di sini kita tidak bisa mencampurkan nilai Hewan dan Hewan Piaraan.
sumber
C ++ 11 FAQ menyebutkan poin-poin di bawah ini:
enum konvensional secara implisit dikonversi ke int, menyebabkan kesalahan ketika seseorang tidak ingin enumerasi bertindak sebagai integer.
Enum konvensional mengekspor enumerator mereka ke ruang lingkup di sekitarnya, menyebabkan bentrokan nama.
Jenis enum yang mendasarinya tidak dapat ditentukan, menyebabkan kebingungan, masalah kompatibilitas, dan membuat pernyataan maju menjadi tidak mungkin.
.
.
sumber
sumber
Perlu dicatat, di atas semua jawaban lain ini, C ++ 20 memecahkan salah satu masalah yang
enum class
ada: verbosity. Membayangkan hipotetis sebuahenum class
,Color
.Ini verbose dibandingkan dengan
enum
variasi biasa , di mana nama-nama berada dalam lingkup global dan karenanya tidak perlu diawali denganColor::
.Namun, di C ++ 20 kita bisa menggunakan
using enum
untuk memperkenalkan semua nama dalam enum ke ruang lingkup saat ini, menyelesaikan masalah.Jadi sekarang, tidak ada alasan untuk tidak menggunakannya
enum class
.sumber
Karena, seperti yang dikatakan dalam jawaban lain, class enum tidak secara implisit dapat dikonversi ke int / bool, itu juga membantu menghindari kode buggy seperti:
sumber
Satu hal yang belum disebutkan secara eksplisit - fitur lingkup memberi Anda opsi untuk memiliki nama yang sama untuk metode enum dan kelas. Contohnya:
sumber