열거 형 클래스가 일반 열거 형보다 선호되는 이유는 무엇입니까?
타입 안전 때문에 C ++에서 enum 클래스 사용을 권장하는 사람들이 몇 명 있었습니다.
그러나 이것이 실제로 무엇을 의미합니까?
C ++에는 두 가지 종류가 있습니다 enum
.
enum class
es- 일반
enum
의
다음은이를 선언하는 몇 가지 예입니다.
enum class Color { red, green, blue }; // enum class
enum Animal { dog, cat, bird, human }; // plain enum
둘의 차이점은 무엇입니까?
enum class
ES - 열거 이름은 지역 열거 및 그 값은 할 수 없습니다 암시 적으로 (다른 같은 다른 형태로 변환enum
또는int
)Plains-
enum
열거 자 이름이 열거와 동일한 범위에 있고 그 값이 암시 적으로 정수 및 다른 유형으로 변환되는 경우
예:
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
잠재적으로 버그로 이어질 수있는 놀라움이 줄어들 기 때문에 es를 선호해야합니다.
에서 비얀 스트로브 스트 룹의 C ++ 11 FAQ :
enum class
ES ( "새 열거 형", "강한 열거 형") 주소를 기존의 C ++ 열거 세 가지 문제를 :
- 일반 열거 형은 암시 적으로 int로 변환되어 열거 형이 정수로 작동하지 않으려는 경우 오류가 발생합니다.
- 기존 열거 형은 열거자를 주변 범위로 내보내 이름 충돌을 일으 킵니다.
- 의 기본 유형을
enum
지정할 수 없으므로 혼동, 호환성 문제가 발생하고 앞으로 선언 할 수 없습니다.새 열거 형은 전통적인 열거 형 (이름 값) 측면과 클래스 측면 (범위 멤버 및 변환 부재)을 결합하기 때문에 "열 클래스"입니다.
따라서 다른 사용자가 언급했듯이 "강력한 열거 형"은 코드를 더 안전하게 만듭니다.
"클래식"의 기본 유형은 enum
모든 값에 맞도록 충분히 큰 정수 유형이어야합니다 enum
. 이것은 일반적으로 int
입니다. 또한 열거 된 각 유형 char
은 부호있는 / 부호없는 정수 유형 과 호환 가능해야합니다 .
이것은 enum
기본 유형 이 무엇인지에 대한 광범위한 설명 이므로 각 컴파일러는 기본 유형의 기본 유형에 대해 스스로 결정을 내릴 enum
수 있으며 때로는 결과가 놀랍습니다.
예를 들어, 나는 이와 같은 코드를 여러 번 보았다.
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?
};
위의 코드에서 일부 순진 코더는 컴파일러가 저장 것이라고 생각 E_MY_FAVOURITE_FRUITS
부호없는 8 비트 타입으로 값을 ...하지만 그것에 대해 어떠한 보증도 없다 : 컴파일러는 선택할 수있다 unsigned char
거나 int
또는 short
그 유형의 모든에 맞게 충분히 큰이며, 에 표시된 값 enum
. 필드를 추가하는 E_MY_FAVOURITE_FRUITS_FORCE8
것은 부담이며 컴파일러가의 기본 유형에 대해 어떤 종류의 선택도하지 않습니다 enum
.
유형 크기에 의존하는 코드가 있거나 E_MY_FAVOURITE_FRUITS
너비가 있다고 가정하면 (예 : 직렬화 루틴)이 코드는 컴파일러의 생각에 따라 이상한 방식으로 작동 할 수 있습니다.
문제를 악화시키기 위해 일부 직장 동료가 부주의하게 우리의 새로운 가치를 부여한다면 enum
:
E_DEVIL_FRUIT = 0x100, // New fruit, with value greater than 8bits
컴파일러는 그것에 대해 불평하지 않습니다! enum
(컴파일러가 가능한 가장 작은 유형을 사용한다고 가정 할 때, 우리가 할 수없는 가정)의 모든 값에 맞게 유형의 크기를 조정합니다 . 이 간단하고 부주의 한 추가는 enum
관련 코드를 깨뜨릴 수있는 미묘한 부분입니다.
C ++ 11은 enum
및에 대한 기본 유형을 지정할 수 있으므로 enum class
( rdb 덕분에 )이 문제는 깔끔하게 해결됩니다.
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
};
필드에이 유형의 범위를 벗어난 표현식이있는 경우 기본 유형을 지정하면 기본 유형을 변경하는 대신 컴파일러가 불평합니다.
이것이 좋은 안전 개선이라고 생각합니다.
그렇다면 왜 열거 형 클래스가 일반 열거 형보다 선호됩니까? scoped ( enum class
) 및 unscoped ( enum
)에 대한 기본 유형을 선택할 수 있다면 enum class
더 나은 선택 은 무엇 입니까? :
- 암시 적으로로 변환되지 않습니다
int
. - 주변 네임 스페이스를 오염시키지 않습니다.
- 그들은 선포 될 수 있습니다.
일반 열거 형에 비해 열거 형 클래스를 사용하는 기본 이점은 두 개의 다른 열거 형에 대해 동일한 열거 형 변수가있을 수 있으며 여전히 해결할 수 있다는 것입니다 ( OP에 의해 안전한 유형 이라고 언급 됨 )
예를 들어 :
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 };
기본 열거 형의 경우 컴파일러는 red
유형을 참조 하는지 Color1
또는 Color2
아래 명령문과 같이 구분할 수 없습니다 .
enum Color1 { red, green, blue };
enum Color2 { red, green, blue };
int x = red; //Compile time error(which red are you refering to??)
열거는 정수 값 집합을 나타내는 데 사용됩니다.
class
뒤에 나오는 키워드 enum
는 열거 형이 강력하게 입력되고 열거 자의 범위가 지정됨을 지정합니다. 이런 식으로 enum
클래스는 실수로 상수를 잘못 사용하는 것을 방지합니다.
예를 들어 :
enum class Animal{Dog, Cat, Tiger};
enum class Pets{Dog, Parrot};
여기서는 Animal과 Pets 값을 혼합 할 수 없습니다.
Animal a = Dog; // Error: which DOG?
Animal a = Pets::Dog // Pets::Dog is not an Animal
C ++ 11 FAQ 는 다음 사항을 언급합니다.
일반 열거 형은 암시 적으로 int로 변환되어 열거 형이 정수로 작동하지 않으려는 경우 오류가 발생합니다.
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;
}
기존 열거 형은 열거자를 주변 범위로 내보내 이름 충돌을 일으 킵니다.
// 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
};
열거 형의 기본 유형을 지정할 수 없으므로 혼동, 호환성 문제가 발생하고 앞으로 선언 할 수 없습니다.
// 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;
}
- 암시 적으로 int로 변환하지 마십시오
- 어떤 유형의 기초를 선택할 수 있습니까
- 오염이 발생하지 않도록 ENUM 네임 스페이스
- 일반 클래스와 비교하여 앞으로 선언 할 수 있지만 메소드가 없습니다.
다른 답변에서 언급했듯이 클래스 열거 형은 암시 적으로 int / bool로 변환 할 수 없으므로 다음과 같은 버그가있는 코드를 피하는 데 도움이됩니다.
enum MyEnum {
Value1,
Value2,
};
...
if (var == Value1 || Value2) // Should be "var == Value2" no error/warning
명시 적으로 언급되지 않은 한 가지-범위 기능은 열거 형과 클래스 메소드에 대해 동일한 이름을 갖는 옵션을 제공합니다. 예를 들어 :
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
};
참고 URL : https://stackoverflow.com/questions/18335861/why-is-enum-class-preferred-over-plain-enum
'Programing' 카테고리의 다른 글
유효성 검증 실패를 위해 REST API 서비스에서 리턴 할 적절한 HTTP 상태 코드는 무엇입니까? (0) | 2020.02.28 |
---|---|
월별 만 표시하는 jQuery UI DatePicker (0) | 2020.02.28 |
std :: promise 란 무엇입니까? (0) | 2020.02.28 |
배열을 JSON으로 변환 (0) | 2020.02.28 |
비동기 화살표 기능의 구문 (0) | 2020.02.28 |