Ⅰ. 객체지향 구조 및 메모리 제어 이론
1. 포인터를 통한 객체 데이터의 영구적 수정 원리
C++에서 변수는 값에 의한 전달(Pass by Value)과 참조/주소에 의한 전달(Pass by Reference/Pointer) 방식으로 나뉜다. Character 클래스가 장착 중인 무기를 Weapon* 형태의 포인터 변수로 관리할 때, 이 변수는 실제 무기 객체가 존재하는 힙(Heap) 메모리 영역의 주소값을 저장한다.
메인 루프에서 해당 주소값을 참조하여 멤버 함수를 호출하고 값을 수정(currentWeapon->SetStats())하면, 복사본이 아닌 메모리 상의 원본 객체가 직접 변경된다. 따라서 함수나 제어 블록이 종료되어 소멸하더라도 변경된 데이터는 메모리에 영구적으로 유지된다.
2. 구조체 백업 패턴을 통한 데이터 안정성 확보 (Safe State Overwrite)
객체의 다수의 속성을 묶어 관리하는 구조체(예: Stats)를 교체할 때는 데이터 유실에 주의해야 한다. 특정 필드(예: 공격력, 레벨)만 수정하기 위해 새로운 구조체 인스턴스를 무작정 생성하여 덮어쓰게 되면, 변경하지 않으려 했던 기존 필드(예: 체력)가 초기화되거나 쓰레기값으로 오염되는 현상이 발생한다.
- 해결 프로토콜: 기존 객체로부터 현재 스탯 구조체를 복사(Read/Backup)받은 후, 수정이 필요한 필드의 값만 변경한 뒤, 이를 다시 객체에 전달(Write/Apply)하는 '선 복사 후 수정' 패턴을 준수해야 안정성이 보장된다.
3. 인벤토리 인터페이스 구현 및 인덱스 보정
동적 배열 데이터 구조인 std::vector<Item*>을 활용하여 사용자에게 목록을 제시하고 선택받는 로직에서는 프로그래밍적 인덱스와 사용자 인덱스의 괴리를 보정해야 한다.
- 출력 단계: 사용자 편의성을 위해 0번 인덱스를 1번으로 변환(i + 1)하여 화면에 출력한다.
- 입력 및 처리 단계: 사용자가 입력한 번호를 배열의 실제 요소와 매칭하기 위해 반드시 1을 감산(input - 1)하여 인덱스를 보정해야 한다. 또한, 존재하지 않는 메모리 공간에 접근하여 발생하는 런타임 에러(Out of Bounds)를 방지하기 위해 0 <= index < vector.size() 조건 검증을 선행해야 한다.
Ⅱ. 연산 및 메모리 순회 메커니즘
4. 연산자 우선순위와 경계값(Boundary Value) 예외 처리
C++ 문법 규격상 곱셈 연산자(*)는 복합 대입 연산자(-=)보다 연산 우선순위가 높다. 따라서 currentGold -= 100 * currentLv; 수식은 우항의 곱셈이 먼저 완전하게 수행된 후 좌항의 뺄셈 및 대입이 진행된다.
단, 장비의 초기 상태인 0레벨(0강) 조건일 때 100 * 0 = 0이 되어 재화 소모가 발생하지 않는 논리적 예외 상황이 발생하므로, 수식에 보정치(currentLv + 1)를 적용하여 최소 비용 경계값을 설정해 주어야 한다.
5. 조건부 아이템 소모 시스템 설계 (Validation & Consumption)
특정 트랜잭션(강화 등) 조건으로 아이템을 소모할 때는 인벤토리 내부를 순회하며 대상을 탐색하는 알고리즘이 요구된다.
반복문을 통해 아이템의 식별자(이름 등)를 대조하고, 조건을 만족하는 아이템을 발견했을 시 동적 할당된 메모리를 해제(delete)하여 메모리 누수(Memory Leak)를 방지한다. 이후 포인터 배열의 연속성을 유지하기 위해 해당 원소를 벡터에서 완전하게 삭제(vector.erase()) 처리한다.
6. C++ 반복자(Iterator)와 다중 역참조 구조의 이해
std::vector<Item*>::iterator는 단순한 포인터 변수가 아니며, 컨테이너의 특정 요소를 가리키는 포인터형 객체(Iterator)이다. 현재 시스템은 포인터를 원소로 갖는 벡터 구조이므로 다음과 같은 다중 구조를 지닌다.
- it: 컨테이너의 특정 칸(메모리 위치)을 가리키는 반복자 주소값.
- *it: 해당 칸의 알맹이를 해제하여 획득한 진짜 아이템 객체의 메모리 주소값 (Item* 타입).
- (*it)->GetName(): 획득한 주소를 다시 한번 참조하여 실제 힙 메모리에 위치한 객체의 멤버 함수를 호출하는 단계.
Ⅲ. switch-case문 문법 및 스코프(Scope) 규칙
8. switch-case문 내부 변수 선언 제한 (skipped by 'case' label)
switch문 내부에 선언된 모든 case 레이블은 별개의 방이 아니며, 단일 스코프(동일한 중괄호 영역)를 공유한다. 따라서 case 1:에서 선언된 변수는 아래에 위치한 case 2: 또는 default: 영역에서도 유효성을 갖는다.
이때 프로그램 흐름이 case 1:을 건너뛰고(skip) 곧바로 하위 레이블로 이동할 경우, 하위 레이블 영역에서는 "선언 및 초기화가 생략된 유령 변수"를 참조하게 되는 문법적 모순이 발생한다. 컴파일러는 이를 방지하기 위해 에러를 발생시키므로, 변수를 선언하는 case 구문은 반드시 별도의 중괄호 { } 블록을 형성하여 변수의 생명주기(Scope)를 제한해야 한다.
9. 제어 변수의 올바른 스코프 배치 규칙
프로그램의 루프 탈출 조건 등을 관장하는 상태 제어 변수(예: isExit)는 switch 내부의 특정 분기점에 종속되어서는 안 된다. 제어 변수가 하위 영역에서 초기화 오류를 유발하지 않고 루프 전체에 영향력을 행사하기 위해서는, switch 블록의 상위 스코프인 while문 직전 영역에 선언 배치하는 것이 아키텍처 관점에서 올바르다.
Ⅳ. 프로그램 안정성 및 상속 이론
10. 방어적 코드 구조 설계 (Fall-through 방지 및 트랜잭션 순서)
- Fall-through 방지: switch-case 구조에서 각 case 블록의 종결 지점에 break;문을 누락할 경우, 제어권이 멈추지 않고 아래 레이블의 코드까지 연속해서 실행하는 논리적 치명상이 발생하므로 제어 흐름을 명확히 끊어주어야 한다.
- 안전한 트랜잭션 순서 보장: 시스템 설계 시 [1단계: 조건 검증(Validation)] ➡️ [2단계: 재화 차감(Transaction)] ➡️ [3단계: 시스템 보상 실행(Execution)]의 순차적 흐름을 엄격히 준수해야 한다. 조건을 완전히 검증하기 전에 소모성 함수를 먼저 배치하면, 이후 조건 탈락 시 데이터를 복구(Rollback)할 수 없는 구조적 결함이 발생한다.
11. 상속 관계에서의 생성자 오버로딩 규칙 (Constructor Delegation)
C++의 상속 메커니즘 상 자식 클래스(파생 클래스)의 객체가 생성될 때는 부모 클래스(기반 클래스)의 생성자가 반드시 먼저 호출되어 선행 조립되어야 한다.
만약 부모 클래스에 매개변수가 정의된 생성자(Item(string, int, int))만 존재한다면, 자식 클래스는 자신의 생성자 초기화 리스트를 통해 부모 클래스가 요구하는 인자의 개수와 타입을 명시적으로 충족시켜 호출해야 한다. 이 규격이 불일치할 시 컴파일러는 일치하는 형태의 함수를 찾을 수 없다는 오버로딩 불일치 에러(no overloaded function takes N arguments)를 반환한다.
'TIL' 카테고리의 다른 글
| [C++] 문자열 대소문자 변환 구현 및 탐색적 학습 방법론 (0) | 2026.05.28 |
|---|---|
| [C++]2차원 벡터(Vector)의 개념과 활용 (0) | 2026.05.27 |
| [TIL] Visual Studio C++ 프로젝트 및 폴더 관리 마스터하기 (필터, OneDrive 충돌, 빌드 제외) (0) | 2026.05.22 |
| [C++] 프로그래머스 - 문자열 내림차순 정렬하기 (sort 함수의 인자와 STL의 구조적 이해) (0) | 2026.05.21 |
| [UE5] 언리얼 엔진 5.5 프로젝트 실행파일(패키징) 만드는 방법 정리 (1) | 2026.05.20 |