Ch06. 복합데이터 - 07. 열거형(enum)

2025. 4. 8. 16:33Programming Language/C++

반응형

1. 열거형(enum)

열거형이란 서로 관련있는 정수, 상수에 이름을 붙여서 사용할 수 있도록 해주는 사용자 정의 자료형이다.

예를 들면 에러 코드값(404, 500, 200, 401 등등)이라던가 요일(월요일, 화요일...일요일)과 같이 서로 연관있는 도메인으로 엮인 정수 혹은 상수값을 묶어주는 자료형이라고 보면 된다.

2. 열거형의 정의 및 선언

1) 열거형의 정의 

열거형은 enum이란 명령어를 사용해서 정의를 하는 데 방식은 아래와 같다.

enum 열거형이름 {값1, 값2, 값3,...};

 

예시를 보여주자면 

enum Color {
    RED, 
    BLUE, 
    YELLOW
};

와 같이 정의할 수 있다.

이렇게 정의된 값의 경우는 순서에 따라서 Default Value로써 

enum Color {
    RED,    //--- 0
    BLUE,   //--- 1
    YELLOW, //--- 2
    . . . . //--- ...
    XXXXX   //--- n-1
}

와 같게 0부터 시작해서 순서대로 int 값이 들어가게 된다.

 

물론 값을 지정해서 정의하는 것도 가능하다.

enum Color {
    RED = 10,
    BLUE = 20, 
    YELLOW = 30
}

 

열거형은 기본적으로 int로 설정되어 있으나 따로 타입의 설정이 가능하다.

enum Color:uint8_t {
    RED, 
    BLUE, 
    YELLOW
};

 

2) 열거형의 선언

정의된 열거형의 기본적인 선언

열거형이름 변수명 = 값;

과 같이 하며 실 사용의 예시는 Color를 예로 들면

Color testColor = RED;

와 같이 사용할 수 있다.

그러면 testColor라는 변수에는 RED에 지정한 값 혹은 순서에 맞는 값이 들어가게 된다.

 

3. 간단한 사용 예시

 

1) 배열의 index로 사용하는 enum

기존에 아래와 같이 사용하던 코드가 있다고 보자.

 

여기서 const로 RED, BLUE, YELLOW라는 색상은 각각 상수로 선언을 했었는데 이렇게 사용하게 되면 해당 값들이 뭐에 대한 내용인지 알 수 가 없다.

그렇기 때문에 이 값들을 Color 라는 열거형으로 만들어주고 

이렇게 그냥 사용만 해주면 자동으로 각각 0, 1, 2 값으로 index의 값에 들어가게 된다.

 

2) 비트 플래그의 값으로 사용 되는 enum

만약 요일별 출석을 체크하는 프로그램이 있을때 이를 비트 플래그를 사용해서 확인한다고 해보자.

 

이전에 비트 하나 하나를 날짜로 잡고 4바이트인 경우 32비트를 갖고 있기 때문에 32일을 체크할 수 있다고 했었는데 

이번엔 7개의 비트만 사용하는 변수 attend를 생성하고 

여기서 7개의 비트를 기준으로 요일별로 출석률을 확인한다고 한다면 

월요일 - 00000001 - 1
화요일 - 00000010 - 2
수요일 - 00000100 - 4
목요일 - 00001000 - 8
금요일 - 00010000 - 16
토요일 - 00100000 - 32
일요일 - 01000000 - 64

와 같이 각 값을 더 해주면서 체크를 했었다 

 

이 값을 enum으로 선언해주고 

이 값을 attend에 더해주면 

해당 비트에 플래그를 세울 수 있고 그러면서 

이렇게 코드를 작성해서 확인해보면

이렇게 결과를 볼 수 있게 된다.

 

4. 열거형의 유의점

열거형의 경우는 

 

이렇게 선언 해두면 전역 상수처럼 그냥 가져다 쓸 수 있게 된다.

그런데 만약 다른 명칭의 열거형의 내부에서 같은 변수를 사용한다면 어떻게 될까 ?

이렇게 에러를 벹는다 

 

이렇게 어떤 열거형의 값으로 가야할지 모르기 때문에 C++11에서는 열거 클래스라는 개념이 추가되었다.

 

5. 열거 클래스(enum class)

열거 클래스는 스코프가 제한된 열거형(scoped enum)으로 기존의 enum은 전역 공간에 상수가 노출되어 이름 충돌 위험이 있었으나, enum class는 이름 충돌 방지, 타입 안전성 강화, 암묵적 변환 방지라는 장점을 제공한다.

 

1) 열거 클래스의 정의

열거 클래스는 기존의 방식에서 단순하게 class 혹은 struct라는 명령어를 추가해주는 것으로 선언이 가능하다

enum class EnumName {
    VALUE1,
    VALUE2,
    ...
};

//     or

enum struct EnumName {
    VALUE1,
    VALUE2,
    ...
};

 

열거 클래스의 경우도 타입의 지정이 가능하다.

enum class EnumName:uint64_t {
    VALUE1,
    VALUE2,
    ...
};

//     or

enum struct EnumName:int64_t {
    VALUE1,
    VALUE2,
    ...
};

 

2) 열거 클래스의 사용

열거 클래스의 경우 스코프를 제한해뒀기 때문에 사용할때 해당 열거 데이터가 어디 스코프를 기준으로 나온 값인지 설정해줘야만 정확한 값을 가져오게 된다.

이렇게 열거 클래스를 두개 선언 했다면 기존처럼 

이렇게 사용하는것은 불가능하고 

이렇게 

열거형명::값;

으로 사용해야한다.

그런데 위에서 그렇게 사용했음에도 불구하고 에러가 발생하는 것을 볼 수 있는데 열거 클래스도 기본적으로 타입을 지정하지 않으면 int인데 왜 이건 에러가 될까?

 

이건 열거 클래스의 경우는 묵시적으로 형변환을 해주지 않기 때문이다.

그렇기에 사용할때 항상 명시적으로 형변환을 해줘야만 한다.

 

3) 열거 클래스에서 묵시적 형변환을 해주지 않는 이유 

이는 C++11의 타입 안정성 강화 설계에 따른 것인데 만약 아래와 같이

이렇게 전혀 상관 없는 열거체가 존재한다고 해보자.

 

이렇게 서로 다른 열거체는 값들이 전혀 다른 의미로써 사용되기 때문에 서로 영향을 줘서는 안될것이다.

그럼에도 불구하고 이 서로 다른 열거체는 

이렇게 덧셈연산이 가능하다

이는 결국 묵시적 형변환을 해주기 때문인데 이렇게 사용이 가능하게 열어둔다면 사용자의 의도와는 다른 결과를 출력하게 될 수 도 있다.

 

이를 방지하기 위해서 C++11에서 생긴 열거 클래스의 경우는 

이렇게 서로 연산이 불가능하게 막혀있다.

 

물론 서로 같은 타입끼리도 덧셈이 불가능하다.

아마 + 연산자에 이를 사용할 수 있도록 매개변수를 오버라이딩 해두지 않았기 때문으로 보인다.

 

이렇게 함으로써 코드의 오류를 컴파일 단계에서 파악할 수 있도록 만들어서 사용자로 하여금 혼란이 없도록 개선한 부분으로 보인다.

 

이렇게 열거 클래스로 사용하는 경우는 원하는 타입으로 명시적 형변환을 해야만 사용이 가능하다라는 점을 알고 있자.

이건 비단 +연산자 - 연산자와 같은 사칙 연산에만 국한되는 내용은 아니다.

대부분의 연산자, 함수에서 통용되는것 같다.

6. 열거형을 선언해서 사용하는 이유

문득 이렇게 사용되는 열거형을 보면서 아니 이러면 그냥 int형 변수를 선언해서 사용하면 안되나? 

이렇게 사용하면 되는거 아닌가? 라는 생각을 했었다.

 

물론 이렇게 사용해도 문제는 없으나 문제는 이 코드가 얼마나 안전하고 유지보수가 쉬운가이다.

int color = (int)Color::RED;

if (color == (int)Color::RED) { ... }

이렇게 사용하면 더 단순하고 직관적이고 컴파일도 잘되고 enum을 int로 바꿔서 비교해서 코드도 단순해진다.

 

그런데 이렇게 사용하면 장기적으로 프로그램을 봤을때 위험할 수 있다.

 

int color = 999;  // 이건 컴파일러가 못 잡아줌

만약 color가 int 타입이라면 enum 범위 밖의 값도 허용이 가능기에 Color에 정의되지 않은 값이 들어올 가능성이 있다는 문제가 있다.

타입 안정성 문제와 IDE 자동 리팩토링이나 타입 추적에서 불리하고, enum에 새 값 추가해도 컴파일 시 자동 경고나 누락 체크 불가하다는 단점이 있기 때문에 사용한다면 

이렇게 사용하는게 더 낫다고 한다.

반응형