[C++] 열거형(enum)
👻 열거형이란?
서로 관련된 상수들을 묶어놓고 편리하게 선언하기 위한 목적으로 만들어진 자료형이다. const
로 상수를 설정하지만 개수가 늘어나게되면 관리하기 힘들어진다. 이를 관리하기 위해 열거형을 사용한다. enum
을 사용한다.
// 일반적인 상수
const int SCISSORS = 1;
const int ROCK = 2;
const int PAPER = 3;
// 열거형을 사용한 관리
// 초기값 미지정시 첫 값은 0
// 그 다음 값들은 이전 값 + 1
enum ENUM_SRP
{
ENUM_SCISSORS = 1,
ENUM_ROCK, // 2
ENUM_PAPER // 3
}
상수는 반드시 초기값을 설정해줘야 하지만 열거형 선언시 초기값은 필수가 아니다. 그대신 초기값을 지정하지 않으면 0부터 시작하여 차례대로 설정된다.
👻 왜 열거형을 사용해야할까?
물론 const
만 사용하여 상수를 지정해 줄 수도 있다. 하지만 서로 관련되어 있는 상수라면 묶음으로 관리하는 것이 훨씬 편하다.
또한 위의 캡쳐와 같이 일반적으로 const
로 선언한 상수의 주소값이 제대로 나오지 않는 것을 확인할 수 있다. 이는 메모리 위에 올라가있지 않은 상태라는 것을 알 수 있다. 경우에 따라서 꼭 올라가지 않는다는 보장은 없다.
👉 컴파일러가 최적화를 할 수 있다면 굳이 상수를 메모리에 올리지 않고 사용이 가능하기 때문이다. 혹, 스택에 올리거나 주소값을 참조한다면 메모리에 올라가게 되는데 그렇게 되면 메모리 낭비가 있을 수도 있다.
하지만 주소값을 알고 싶으면
auto
라는 명령어를 사용해 알 수 있는 방법이 있다. 자세한 이야기는 뒤에서 다룰 예정이니 지금은 참고만 하자.auto a = &SCISSORS;
하지만 열거형은 빌드 단계에서 모두 정해진 값으로 치환되기 때문에 각 값을 완벽히 대체할 수 있다. 위 코드를 예시로 들면 1 그 자체가 ENUM_SCISSORS라는 단어로 대체되기 때문에 메모리를 상수보다는 효율적으로 관리를 할 수 있게 된다.
👻 #define
상수를 만드는 또 다른 방법 중 하나는 #define
이다. 앞부분에 #
이 붙은 것들은 전처리 지시문이라고 이야기한다.
빌드는 전처리 👉 컴파일 👉 링크
방식으로 진행되는데, 컴파일 전이 전처리 단계다. 전처리 지시문은 전처리 단계에서 진행되는 명령문이라고 볼 수 있다.
💡
#include <iostream>
iostream
이라는 파일을 찾아서 해당 내용을 복붙해달라는 의미이다.
#define DEFINE_SCISSORS 1
#define DEFINE_TEST cout << "Hello World" << endl; // Hello World가 출력된다
코드를 바꿔치기 하는 개념이 크다.
하지만 #define
선언 방법은 값을 그대로 바꿔치기 때문에 생각보다 모호한 상황이 많이 발생한다. 다른 상수 선언 방법을 우선적으로 사용하는 게 좋으며 현재 #define
사용은 지양하는 편이 좋다.
#define DEFINE_SCISSORS 1+2
int main()
{
int result = DEFINE_SCISSORS * 2;
cout << result << endl;
}
위의 코드를 실행시키면 result
의 값은 6이 될 것 같지만 5가 나오게 된다. 1+2
문자 그대로를 치환하기 때문에 연산 순서에 의해서 1 + (2 * 2)
로 실행되기 때문이다.
또한 전처리 단계에서 실행되면서 코드가 확장되기 때문에 브레이크 포인트를 잡은 후 디버그하기 어렵다. 👉 이미 치환되어 버렸기 때문에 값을 알 수 없다.
👻 글을 마치며
이번 시간에는 열거형을 포함해 상수 선언 방법에 대해 정리해보았다. enum도 어디서 많이 보긴 했었는데 이번 시간에 개념을 확실하게 정리한 것 같고 다른 선언 방법(const나 define)과의 차이점에 대해서도 확실하게 알 수 있었다. 이렇게 차이점을 다 알아두면 언제 어디서 무엇을 써야할 지 선택하기 편해질 것 같다.
Leave a comment