Enum kullanmayı tavsiye eden birkaç kişi duydum tür güvenliği nedeniyle C ++ sınıfları .
Ama bu gerçekten ne anlama geliyor?
Enum kullanmayı tavsiye eden birkaç kişi duydum tür güvenliği nedeniyle C ++ sınıfları .
Ama bu gerçekten ne anlama geliyor?
Yanıtlar:
C ++ iki çeşittir enum
:
enum class
esenum
sOnları nasıl ilan edeceğinize dair birkaç örnek:
enum class Color { red, green, blue }; // enum class
enum Animal { dog, cat, bird, human }; // plain enum
İkisi arasındaki fark nedir?
enum class
es - listeleyicisi isimleri yerel enum ve değerleri yok değil örtülü (başka gibi diğer türlerine dönüştürmek enum
ya int
)
Düz enum
s - numaralandırıcı adlarının enum ile aynı kapsamda olduğu ve değerlerinin dolaylı olarak tamsayılara ve diğer türlere dönüştüğü
Misal:
enum Color { red, green, blue }; // plain enum
enum Card { red_card, green_card, yellow_card }; // another plain enum
enum class Animal { dog, deer, cat, bird, human }; // enum class
enum class Mammal { kangaroo, deer, human }; // another enum class
void fun() {
// examples of bad use of plain enums:
Color color = Color::red;
Card card = Card::green_card;
int num = color; // no problem
if (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; // error
if (m == a) // error (good)
cout << "bad" << endl;
if (a == Mammal::deer) // error (good)
cout << "bad" << endl;
}
enum class
potansiyel olarak hatalara yol açabilecek daha az sürprize neden oldukları için tercih edilmelidir.
A
devletli bir sınıfım varsa ve sınıfın enum class State { online, offline };
çocuğu olarak bir sınıf oluşturursam A
, bunun yerine state == online
içeride kontroller yapmak istiyorum ... bu mümkün mü? A
state == State::online
enum class
onu ortadan kaldırmaktı.
Color color = Color::red
?
if (color == Card::red_card)
hat, 4 satır sonra açıklama daha (Şimdi gördüğüm bloğun ilk yarısında için de geçerlidir.) Bloğun 2 satır verir kötü örnekler. İlk 3 satır sorun değil. "Tüm blok neden düz numaralandırmalar kötü" beni de attı, ben de bu konuda bir şeyler yanlış olduğunu düşündüm. Şimdi görüyorum, bu sadece bir kurulum. Her durumda, geri bildirim için teşekkürler.
Gönderen Bjarne Stroustrup'un C ++ 11 SSS :
enum class
Es ( "Yeni çeteleler", "güçlü çeteleler") adresi geleneksel C ++ numaralandırma ile üç sorunlar:
- konvansiyonel numaralandırmalar dolaylı olarak int'e dönüştürülür ve bir numaralandırma bir tamsayı gibi davranmak istemediğinde hatalara neden olur.
- konvansiyonel numaralandırmalar numaralandırıcılarını çevre kapsamına ihraç ederek isim çatışmalarına neden olmaktadır.
- altta yatan tür
enum
belirtilemez, karışıklığa, uyumluluk sorunlarına neden olur ve ileri bildirimi imkansız hale getirir.Yeni numaralandırmalar "numaralandırma sınıfı" dır çünkü geleneksel numaralandırmaların (isim değerleri) yönlerini sınıfların yönleriyle (kapsam dahilindeki üyeler ve dönüşüm yokluğu) birleştirirler.
Yani, diğer kullanıcılar tarafından belirtildiği gibi, "güçlü numaralandırmalar" kodu daha güvenli hale getirir.
Bir "klasik" enum
öğesinin temel türü, tüm değerlerine uyacak kadar büyük bir tamsayı türü olmalıdır enum
; bu genellikle bir int
. Ayrıca her numaralandırılmış tip aşağıdakilerle uyumlu olmalıdırchar
işaretli / işaretsiz tam sayı tipiyle .
Bu, enum
altta yatan bir türün ne olması gerektiğinin geniş bir açıklamasıdır , bu nedenle her derleyici, klasikin altta yatan türü hakkında kendi başına kararlar alır enum
ve bazen sonuç şaşırtıcı olabilir.
Örneğin, bunun gibi bir sürü kez kod gördüm:
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?
};
Yukarıdaki kod, bazı naif kodlayıcı derleyici saklayacak düşünerek olan E_MY_FAVOURITE_FRUITS
işaretsiz 8bit türü içine değerleri ... ama bu konuda hiçbir garanti yoktur: derleyici tercih edebilir unsigned char
ya da int
ya da short
bu türden olan tüm sığdırmak için yeterince büyük olan, değerleri enum
. Alanın eklenmesi E_MY_FAVOURITE_FRUITS_FORCE8
bir yüktür ve derleyiciyi temel türü hakkında herhangi bir seçim yapmaya zorlamaz enum
.
Tür boyutuna dayanan bir kod parçası varsa ve / veya E_MY_FAVOURITE_FRUITS
bazı genişlikte olabileceğini varsayarsa (örneğin: serileştirme rutinleri) bu kod, derleyici düşüncelerine bağlı olarak bazı garip yollarla davranabilir.
Ve bazı iş arkadaşları dikkatsizce yeni bir değer katıyorsa, işleri daha da kötüleştirmek için enum
:
E_DEVIL_FRUIT = 0x100, // New fruit, with value greater than 8bits
Derleyici bundan şikayetçi değil! Yalnızca türün tüm değerlerine uyacak şekilde yeniden boyutlandırır enum
(derleyicinin mümkün olan en küçük türü kullandığını varsayarsak, yapamayacağımız bir varsayımdır). Bu basit ve dikkatsiz ekleme enum
, incelikle ilgili kodu kırabilir.
C ++ 11 enum
ve enum class
(teşekkürler rdb ) için temel türü belirtmek mümkün olduğundan, bu sorun düzgün bir şekilde ele alınır:
enum class E_MY_FAVOURITE_FRUITS : unsigned char
{
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_DEVIL_FRUIT = 0x100, // Warning!: constant value truncated
};
Bir alanın bu tür aralığın dışında bir ifadesi varsa temel türü belirtmek, derleyicinin altta yatan türü değiştirmek yerine şikayet edecektir.
Bunun iyi bir güvenlik iyileştirmesi olduğunu düşünüyorum.
Peki neden enum sınıfı düz enum'a göre tercih ediliyor? , scoped ( enum class
) ve uncoped ( enum
) numaralar için temel türü seçebilirsek, başka ne enum class
daha iyi bir seçim yapar ? :
int
.Enum sınıfını normal numaralandırmalara göre kullanmanın temel avantajı, 2 farklı numaralandırma için aynı numaralandırma değişkenlerine sahip olabilmeniz ve yine de bunları çözebilmenizdir ( OP tarafından güvenli tip olarak belirtilmiştir )
Örneğin:
enum class Color1 { red, green, blue }; //this will compile
enum class Color2 { red, green, blue };
enum Color1 { red, green, blue }; //this will not compile
enum Color2 { red, green, blue };
Temel numaralandırmalara gelince, derleyici red
tipe Color1
veya Color2
aşağıdaki ifadede olduğu gibi olup olmadığını ayırt edemeyecektir .
enum Color1 { red, green, blue };
enum Color2 { red, green, blue };
int x = red; //Compile time error(which red are you refering to??)
enum { COLOR1_RED, COLOR1_GREE, COLOR1_BLUE }
ad alanı sorunlarını kolayca giderirsiniz. İsim alanı argümanı, burada bahsetmediğim üçünden biri.
enum Color1 { COLOR1_RED, COLOR1_GREEN, COLOR1_BLUE }
Enum sınıfı: ile karşılaştırılabilir enum class Color1 { RED, GREEN, BLUE }
. Erişim benzerdir: COLOR1_RED
vs Color1::RED
, ancak Enum sürümü her değerde "COLOR1" yazmanızı gerektirir; bu da bir enum sınıfının ad alanı davranışının önlediği yazım hataları için daha fazla alan sağlar.
enum Color1
, bir derleyicinin yakalayamayacağı değerleri başlangıçta tanımladığınız zaman, muhtemelen 'geçerli' bir isim olacağı anlamına gelir. Ben yazarsam RED
, GREEN
ve bu yüzden hiç çözemezse daha bir enum sınıfını kullanma konusunda enum Banana
belirttiğiniz gerektirdiğinden Color1::RED
değeri (ad argümanı) erişmek için. Kullanmak için hala iyi zamanlar var enum
, ancak bir 'nin ad alanı davranışı enum class
genellikle çok yararlı olabilir.
Numaralandırmalar bir tamsayı değer kümesini temsil etmek için kullanılır.
Ardından class
anahtar sözcük enum
, numaralandırmanın güçlü bir şekilde yazıldığını ve numaralandırıcılarının kapsamlandığını belirtir. Bu şekilde enum
sınıflar sabitlerin kazara yanlış kullanımını önler.
Örneğin:
enum class Animal{Dog, Cat, Tiger};
enum class Pets{Dog, Parrot};
Burada Hayvan ve Evcil Hayvan değerlerini karıştıramayız.
Animal a = Dog; // Error: which DOG?
Animal a = Pets::Dog // Pets::Dog is not an Animal
C ++ 11 SSS aşağıdaki noktalardan bahseder:
konvansiyonel numaralandırmalar dolaylı olarak int'e dönüştürülür ve bir numaralandırma bir tamsayı gibi davranmak istemediğinde hatalara neden olur.
enum color
{
Red,
Green,
Yellow
};
enum class NewColor
{
Red_1,
Green_1,
Yellow_1
};
int main()
{
//! Implicit conversion is possible
int i = Red;
//! Need enum class name followed by access specifier. Ex: NewColor::Red_1
int 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'
return 0;
}
konvansiyonel numaralandırmalar numaralandırıcılarını çevre kapsamına ihraç ederek isim çatışmalarına neden olmaktadır.
// Header.h
enum vehicle
{
Car,
Bus,
Bike,
Autorickshow
};
enum FourWheeler
{
Car, // error C2365: 'Car': redefinition; previous definition was 'enumerator'
SmallBus
};
enum class Editor
{
vim,
eclipes,
VisualStudio
};
enum class CppEditor
{
eclipes, // No error of redefinitions
VisualStudio, // No error of redefinitions
QtCreator
};
Temel numaralandırma türü belirtilemez, karışıklığa, uyumluluk sorunlarına neden olur ve ileri bildirimi imkansız hale getirir.
// Header1.h
#include <iostream>
using namespace std;
enum class Port : unsigned char; // Forward declare
class MyClass
{
public:
void PrintPort(enum class Port p);
};
void MyClass::PrintPort(enum class Port p)
{
cout << (int)p << endl;
}
.
// Header.h
enum class Port : unsigned char // Declare enum type explicitly
{
PORT_1 = 0x01,
PORT_2 = 0x02,
PORT_3 = 0x04
};
.
// Source.cpp
#include "Header1.h"
#include "Header.h"
using namespace std;
int main()
{
MyClass m;
m.PrintPort(Port::PORT_1);
return 0;
}
Bu diğer cevapların üstünde, C ++ 20'nin sahip olduğu sorunlardan birini çözdüğünü belirtmek gerekir enum class
: ayrıntı düzeyi. Farazi hayal enum class
, Color
.
void foo(Color c)
switch (c) {
case Color::Red: ...;
case Color::Green: ...;
case Color::Blue: ...;
// etc
}
}
Bu, enum
isimlerin global kapsamda olduğu ve bu nedenle ön ekin eklenmesi gerekmediği düz varyasyonla karşılaştırıldığında ayrıntılıdır Color::
.
Bununla birlikte, C ++ 20'de using enum
bir enumdaki tüm isimleri mevcut kapsama tanıtmak ve sorunu çözmek için kullanabiliriz.
void foo(Color c)
using enum Color;
switch (c) {
case Red: ...;
case Green: ...;
case Blue: ...;
// etc
}
}
Yani şimdi, kullanmamak için hiçbir neden yok enum class
.
Diğer yanıtlarda belirtildiği gibi, sınıf enum int / bool'a dolaylı olarak dönüştürülemediğinden, buggy kodundan kaçınmaya da yardımcı olur:
enum MyEnum {
Value1,
Value2,
};
...
if (var == Value1 || Value2) // Should be "var == Value2" no error/warning
Açıkça belirtilmemiş bir şey - kapsam özelliği size bir numaralandırma ve sınıf yöntemi için aynı ada sahip olma seçeneği sunar. Örneğin:
class Test
{
public:
// these call ProcessCommand() internally
void TakeSnapshot();
void RestoreSnapshot();
private:
enum class Command // wouldn't be possible without 'class'
{
TakeSnapshot,
RestoreSnapshot
};
void ProcessCommand(Command cmd); // signal the other thread or whatever
};