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 classesenumsOnları 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 classes - listeleyicisi isimleri yerel enum ve değerleri yok değil örtülü (başka gibi diğer türlerine dönüştürmek enumya int)
Düz enums - 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 classpotansiyel olarak hatalara yol açabilecek daha az sürprize neden oldukları için tercih edilmelidir.
Adevletli 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 == onlineiçeride kontroller yapmak istiyorum ... bu mümkün mü? Astate == State::online
enum classonu 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 classEs ( "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
enumbelirtilemez, 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, enumaltta 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 enumve 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_FRUITSişaretsiz 8bit türü içine değerleri ... ama bu konuda hiçbir garanti yoktur: derleyici tercih edebilir unsigned charya da intya da shortbu 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_FORCE8bir 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_FRUITSbazı 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 enumve 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 classdaha 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 redtipe Color1veya Color2aş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_REDvs 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, GREENve bu yüzden hiç çözemezse daha bir enum sınıfını kullanma konusunda enum Bananabelirttiğiniz gerektirdiğinden Color1::REDdeğeri (ad argümanı) erişmek için. Kullanmak için hala iyi zamanlar var enum, ancak bir 'nin ad alanı davranışı enum classgenellikle çok yararlı olabilir.
Numaralandırmalar bir tamsayı değer kümesini temsil etmek için kullanılır.
Ardından classanahtar 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 enumsı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, enumisimlerin 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 enumbir 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
};