Enumeration (enum)
열거체
- 기호 상수를 만드는 방법 중 하나이다.
- Enumeration(열거체)은 enum형 변수를 의미한다.
- 열거체에 적용되는 규칙은 꽤 엄격한 편이라서 열거체를 새로운 데이터형을 정의하는 수단으로 활용하기 보다는, 상호 관련이 있는 기호 상수들을 정의하는 용도로 쓰인다.
Rules
Ex. enum decalration
enum spectrum {red, orange, yellow, green, blue, violet};
spectrum : 키워드 "enum"을 통해 만들어진 새로운 데이터형
red...violet : 0 부터 n-1까지의 정수 값에 각각 대응되는 기호 상수들, 이 상수들을 Enumerator(열거자)라고 한다.
차례대로 red는 0, orange는 1, violet은 5에 대응된다.
(값을 명시하지 않을 경우에 0 부터 n-1까지의 수치로 대응되는 것이다.)
Ex. Enumeration Usage
spectrume band;
band = blue;
band = spectrum(3); // 열거체 band에 green에 해당되는 값이 대입된다.
band = 3; // Error! 3을 직접 대입할 수 없다! (인덱스 값으로 인식하지 않는다!)
band = 2000; // Error! 2000은 열거자가 아니다!
++band; // Error! 열거체는 대입 연산자만 사용되도록 정의되어있다.
band = orange + red; // Error! 열거체는 대입 연산자만 사용되도록 정의되어있다.
- 열거체 변수에는 해당 데이터형을 선언하는 데 사용된 열거자 값들만 대입시킬 수 있다.
- 열거체는 대입 연산자만을 허용한다. 다른 연산을 일체 금지된다.
※ 일부 C++에서는 다른 연산자를 허용하는 경우도 있다. 하지만 코드의 이식성을 제고시키기 위해서 문법을 지키는 것이 좋다.
Ex. Anonymous Enumeration Data type
enum {red, orange, yellow, green, blue, violet};
int color[10];
cout << color[red];
- 열거체 변수를 만들지 않고 오직 기호 상수만을 사용하고자 한다면 열거체 데이터형의 이름을 생략할 수 있다.
Ex. Setting Enumerator Values
enum bits {one=1, two=2, four=4, eight=8}; // 명시적인 값 지정이 가능하다.
enum bigstep {first, second=100, third}; // first에는 0, third에는 100이 대입된다.
enum {zero, null=0, one, uno=1}; // 여러 열거자에 대해 같은값으로 지정이 가능하다.
- 열거자의 값을 명시적으로, 순서를 지키지 않고 지정할 수 있다.
- 모든 열거자가 아닌, 일부 열거자에만 명시적으로 값을 지정할 수도 있으며, 이 경우에는 초기화되지 않는 열거자들은 바로 앞의 열거자에서 1씩 더해져서 대입된다.
- 동시에 여러 열거자들에 대해 같은 값으로 초기화가 가능하다.
- 이 모든 경우에서 프로그래머에 의해 대입되는 값들은 정수로 한정된다.
(과거 C++초기버전에서는 열거자에 대입되는 값이 int형으로 한정되어 있었는데, 추후에 long형까지 확장되었다.
Summary
- 열거체 변수에는 해당 데이터형을 선언하는 데 사용된 열거자 값들만 대입시킬 수 있다.
- 열거체는 대입 연산자만을 허용한다. 다른 연산을 일체 금지된다.
- 열거체 변수를 만들지 않고 오직 기호 상수만을 사용하고자 한다면 열거체 데이터형의 이름을 생략할 수 있다.
열거자의 값을 명시적으로 지정할 수 있다.
- 열거자의 값을 명시적으로, 순서를 지키지 않고 프로그래머 임의대로 지정(초기화)할 수 있다.
- 모든 열거자가 아닌, 일부 열거자에만 명시적으로 값을 지정할 수도 있으며, 이 경우에는 초기화되지 않는 열거자들은 바로 앞의 열거자에서 1씩 더해져서 대입된다.
- 동시에 여러 열거자들에 대해 같은 값으로 초기화가 가능하다.
- 열거자에 대입되는 값들은 정수로 한정된다.
Range of Enumeration Values
- 각 열거체는 값 범위를 가지며, 임의의 정수값이 그 범위 안에 들어온다면, 명시되지 않은 열거자 값이더라도 데이터형 변환을 통해 열거체 변수에 대입할 수 있다.
- 시스템이 열거체 값의 범위를 정의하는 것은 컴파일러가 열거체를 위해 얼만큼의 메모리를 할당하느냐와 관계가 있다.
Algorithm to Find the Range of Enumeration Values
Description
범위의 상한 : 열거자 값 중 최댓값을 취한 후, 이 최댓값보다 큰 2의 최소 거듭제곱을 구하고 여기에 1을 뺀 수치가 상한이 된다.
범위의 하한 : 열거자 값 중 최솟값을 취한 후, 이 최솟값이 0보다 크거나 같다면 0이 하한이 되며, 최솟값이 음수인 경우에는 상한을 구할 때와 같은 방법으로 구하고 마이너스 부호를 붙이면 그 값이 하한이 된다.
Pseudo code
\(DomainFinder\ (int\ M, int\ m)\\ \mathrm{"M"\ means\ The\ maximum\ value\ in\ enumerators.}\\\mathrm{"m"\ means\ The\ minimum\ value\ in\ enumerators.}\\Let,\ 2^k \leq M \leq 2^{k+1} \quad (k \in \mathit{Z})\\if\ M\ =\ 2^k\\\quad then,\ UpperBound\ =\ 2^{k+1}-1\\else\ if\ M\ =\ 2^{k+1}\\\quad then,\ UpperBound\ =\ 2^{(k+1)+1}-1\\else \quad in\ case,\ 2^k < M < 2^{k+1}\\\quad then, UpperBound\ =\ 2^{k+1}-1\\\mathrm{Complete\ to\ find\ the\ Greatest\ Upper\ Bound\ !}\\
if\ m \geq 0\\
\quad then,\ LowerBound\ =\ 0\\
Let,\ 2^k \leq -m \leq 2^{k+1}\\
if\ m\ =\ 2^k\\
\quad then,\ LowerBound\ =\ -(2^{k+1}-1)\\
else\ if\ m\ =\ 2^{k+1}\\
\quad then,\ LowerBound\ =\ -(2^{(k+1)+1}-1)\\
else \quad in\ case,\ 2^k < -m < 2^{k+1}\\
\quad then,\ LowerBound\ =\ -(2^{k+1}-1)\\
\mathrm{Complete\ to\ find\ the\ Greatest\ Lower\ Bound\ !}\)
Scoped Enumeration (범위가 정해진 열거체)
- 두 개 이상의 열거체에서 서로 열거자의 이름이 겹칠 경우에 생기는 문제에 대한 해결책으로, C++11에서 새로 추가되었다.
- 열거자에 Class Scope(클래스 사용 범위)를 부여하여 다른 클래스(열거체)의 열거자들의 접근을 막아 서로를 은닉시키는 원리이다.
- 이제 열거자는 클래스 사용 범위를 가졌기 때문에, 열거체 외부에서는 열거자에 접근하고자 하는 경우 Scope Resolution Operator (\(\texttt{::}\) 연산자; 범위 결정 연산자)를 이용하여 어떤 열거체에 열거자인지까지 명시해야 한다.
// Ex. 범위가 정해진 열거체 사용 예시
enum egg {Small, Medium, Large, Jumbo};
enum t_shirt {Small, Medium, Large, XLarge};
// 전통적인 열거체, 열거자를 사용할 경우 이름 간 중복의 문제가 생긴다.
enum class egg {Small, Medium, Large, Jumbo};
enum class t_shirt {Small, Medium, Large, XLarge};
// Scoped Enumeration, 열거자들이 Class Scope를 갖는다.
// 이 때, class 키워드 대신 struct 키워드로 사용할 수도 있다!
egg choice = egg::Large; // 어떤 열거체의 열거자인지 명시한다.
t_shirt Floyd = t_shirt::Large; // 어떤 열거체의 열거자인지 명시한다.
- 범위가 정해진 열거에서는 \(\texttt{int}\)형으로의 암시적 데이터형 변환이 일어나지 않으며, 필요한 경우에는 명시적 형변환은 허용한다.
// Ex. 범위가 정해진 열거에서의 형변환 예시
enum egg_old {Small, Medium, Large, Jumbo}; // 전통적인 열거체
enum class t_shirt {Small, Medium, Large, XLarge}; // 범위가 정해진 열거체
egg_old one = Medium;
t_shirt rolf = t_shirt::Large;
int king = one; // 전통적인 열거자에게는 가능하다.
int ring = rolf; // 범위가 정해진 열거체이기 때문에 불가능하다.
int Frodo = int(t_shirt::Small); // 명시적 형변환, int Frodo = 0; 구문과 같다.
if (king < Jumbo ) // 허용되는 표현식이다.
...
if (king < t_shirt::Medium) // 허용되지 않는다.
...
- 범위가 정해진 열거에서의 Built-in Data Type은 \(\texttt{int}\)형 이지만, 사용자에 따라 다른 정수형 데이터형으로 선택할 수 있다.
enum class : short pizza {Small, Medium, Large, XLarge};
// 열거체 태그(pizza) 앞에 데이터 형의 종류를 명시하여 열거체의 내재 데이터형을 정의한다.