C++ 공부

[C++ Study] 열거형, 복사 생성자, 그리고 메모리 관리 전략

Client Side 2026. 4. 8. 13:00

1. enum vs enum class (Scoped Enum)

현대 C++(C++11 이상) 및 언리얼 엔진 환경에서 enum class 사용은 선택이 아닌 필수입니다.

📋 주요 차이점 비교

구분 enum (Unscoped) enum class (Scoped)
유효 범위 전역(Global) 범위에 노출됨 클래스 범위 내로 제한됨
타입 안전성 정수형(int)과 암시적 변환 가능 암시적 변환 불가 (명시적 캐스팅 필요)
이름 충돌 다른 enum과 이름 중복 불가 이름 중복 가능 (Scope로 구분)
메모리 지정 컴파일러가 결정 (불투명함) Underlying Type(uint8 등) 지정 가능

💡 지식의 확장: 왜 enum class인가?

  • 타입 안정성: 실수로 열거형 값을 정수 함수 인자에 넣는 치명적 버그를 컴파일 타임에 차단합니다.
  • 메모리 최적화: 특히 모바일 환경에서는 enum class EState : uint8과 같이 타입을 명시하여 메모리 대역폭을 절약할 수 있습니다.
  • 한계 인식: 출력 시 static_cast<int>()를 거쳐야 하는 번거로움이 있지만, 이는 대규모 프로젝트에서 '코드의 명확성'을 보장하는 비용입니다.

2. 복사 생성자 (Copy Constructor)

객체가 복사될 때 호출되는 생성자로, 특히 포인터 멤버가 있는 클래스에서 매우 중요하게 다뤄야 합니다.

⚠️ 얕은 복사(Shallow Copy)의 위험성

디폴트 복사 생성자는 포인터의 '주소값'만 복사합니다. 이로 인해 세 가지 치명적인 문제가 발생합니다.

  1. 중복 해제(Double Free): 두 객체가 같은 주소를 해제하려다 크래시 발생.
  2. 댕글링 포인터(Dangling Pointer): 한 객체가 메모리를 해제했는데 다른 객체가 그곳을 참조함.
  3. 의도치 않은 값 변경: 한 객체의 수정이 다른 객체에 영향을 줌.

🔄 복사 생성자가 호출되는 3가지 시점

  • 기존 객체로 새로운 객체를 초기화할 때 (T a = b;)
  • 함수에 인자를 값 전달(Pass by Value) 방식으로 넘길 때
  • 함수에서 객체를 값으로 반환할 때 (단, 최신 컴파일러는 RVO로 최적화하기도 함)

3. 문자열 관리와 메모리 최적화

C++의 기본 문자열 처리와 언리얼 엔진의 자동화 시스템 비교입니다.

💎 깊은 복사(Deep Copy)를 통한 문자열 관리

  • 직접 구현: new char[len + 1]을 통해 각 객체가 독립적인 메모리 공간을 가지도록 설계합니다.
  • 언리얼 엔진(FString): 내부에 TArray<TCHAR>를 가지며, 예약 할당(Slack)을 통해 빈번한 재할당 성능 저하를 막습니다.

🚀 최적화 대안 (Unreal Engine 기준)

  • FName: 해시 테이블을 사용하여 비교 연산이 O(1)입니다. 수정이 필요 없는 아이템 이름, 태그 등에 적합합니다.
  • FText: 다국어 지원 및 로컬라이징이 필요한 UI 텍스트에 사용합니다.
  • FString::Reserve(): 문자열이 커질 것을 미리 안다면 한 번만 할당받아 힙 파편화를 방지합니다.

4. 방어적 프로그래밍: ensure vs IsValid

런타임 안정성을 확보하기 위한 두 가지 핵심 도구입니다.

도구 성격 사용 목적 배포(Shipping) 시
ensure(조건) 디버깅용 경고 "이곳에 Null이 오면 설계 오류다"를 알림 코드 증발 (성능 영향 없음)
IsValid(객체) 실행 로직용 분기 "객체가 사라졌을 수 있으니 확인 후 실행" 항상 작동

Senior's Tip: 모든 곳에 if (IsValid)만 쓰면 버그가 로그 없이 묻힐 수 있습니다. 설계상 완벽해야 하는 곳엔 ensure나 check를 써서 즉시 문제를 파악해야 합니다.


5. 임시 객체(Temporary Object)와 이동 시맨틱

눈에 보이지 않는 성능 도둑, 임시 객체를 제어하는 방법입니다.

📦 임시 객체가 생성되는 이유

함수가 결과물을 반환할 때, 지역 변수는 파괴되므로 이를 안전하게 전달하기 위한 '대피소'로서 임시 객체가 생성됩니다.

🛠️ 성능 저하를 막는 필살기

  1. 참조자(&) 활용: 인자를 전달할 때 const T&를 사용하여 복사 생성자 호출을 원천 차단합니다.
  2. 이동 시맨틱(Move Semantics): 데이터를 복사하지 않고 소유권만 이전합니다. (TMoveTemp)
  3. RVO (Return Value Optimization): 컴파일러가 반환 값을 생성 위치에 바로 구축하도록 유도합니다.