Topic 1: 예외 처리 기본 🚨
🎯 학습 목표
- 예외 처리의 필요성과 개념을 이해할 수 있다
- try-catch-throw 문법을 활용하여 예외를 처리할 수 있다
- 표준 예외 클래스들의 종류와 특징을 파악할 수 있다
- 게임 프로그래밍에서 발생하는 실제 오류를 예외 처리로 해결할 수 있다
- 안전한 프로그램 작성을 위한 예외 처리 패턴을 적용할 수 있다
🛡️ 안전망으로서의 예외 처리
프로그램에서 예상치 못한 오류는 언제든 발생할 수 있어요! 💥
서커스 곡예사의 안전망 비유:
-
정상 실행 = 곡예사의 공연 🎪
- 계획된 대로 멋지게 진행
- 모든 것이 완벽할 때
-
예외 상황 = 곡예사가 떨어짐 😱
- 예상치 못한 실수나 문제
- 프로그램이 갑자기 멈춤
-
예외 처리 = 안전망 🛡️
- 떨어져도 안전하게 받아줌
- 프로그램이 비정상 종료되지 않고 복구
// 예외 처리 없는 경우 (위험!)
int result = 10 / 0; // 💥 프로그램 크래시!
// 예외 처리가 있는 경우 (안전!)
try {
int result = 10 / 0;
} catch (...) {
cout << "🛡️ 안전망이 작동했습니다!" << endl;
}🚨 예외 처리의 3요소
C++에서 예외 처리는 3가지 키워드로 구성됩니다:
1. try - 위험 구간 설정 ⚠️
try {
// 예외가 발생할 수 있는 코드
// "이 부분에서 문제가 생길 수 있어요!"
}2. catch - 예외 포착 🎣
catch (예외타입 e) {
// 예외가 발생했을 때 실행할 코드
// "문제가 생기면 이렇게 해결하겠습니다!"
}3. throw - 예외 던지기 💣
throw 예외객체;
// "문제가 발생했음을 알립니다!"🎮 첫 번째 예외 처리 예제
게임 캐릭터 레벨업 시스템
#include <iostream>
#include <stdexcept> // 표준 예외 클래스
using namespace std;
class GameCharacter {
private:
string name;
int level;
int exp;
public:
GameCharacter(string n) : name(n), level(1), exp(0) {}
void gainExp(int amount) {
if (amount < 0) {
// 음수 경험치는 허용하지 않음
throw invalid_argument("경험치는 음수가 될 수 없습니다!");
}
exp += amount;
cout << name << "이(가) " << amount << " 경험치를 획득했습니다! ✨" << endl;
// 레벨업 체크
if (exp >= 100) {
levelUp();
}
}
void levelUp() {
if (level >= 100) {
throw runtime_error("최대 레벨에 도달했습니다!");
}
level++;
exp = 0;
cout << "🎉 " << name << "이(가) 레벨 " << level << "로 레벨업했습니다!" << endl;
}
void showStatus() {
cout << "🎮 캐릭터: " << name << " | 레벨: " << level << " | 경험치: " << exp << "/100" << endl;
}
};
int main() {
cout << "🎮 RPG 게임 시스템 🎮" << endl;
cout << "=====================" << endl;
GameCharacter hero("용감한전사");
hero.showStatus();
cout << endl;
// 정상적인 경험치 획득
try {
cout << "📈 경험치 획득 테스트:" << endl;
hero.gainExp(30);
hero.gainExp(25);
hero.gainExp(50); // 레벨업 발생
hero.showStatus();
cout << endl;
} catch (const invalid_argument& e) {
cout << "❌ 입력 오류: " << e.what() << endl;
} catch (const runtime_error& e) {
cout << "⚠️ 실행 오류: " << e.what() << endl;
}
// 예외 상황 1: 음수 경험치
try {
cout << "🚨 음수 경험치 테스트:" << endl;
hero.gainExp(-10); // 예외 발생!
} catch (const invalid_argument& e) {
cout << "❌ 잘못된 입력: " << e.what() << endl;
cout << "✅ 시스템이 안전하게 처리했습니다!" << endl;
}
cout << endl;
// 예외 상황 2: 최대 레벨 도달
try {
cout << "🚨 최대 레벨 테스트:" << endl;
// 강제로 레벨 99로 설정 후 레벨업 시도
for (int i = 0; i < 98; i++) {
hero.gainExp(100); // 강제 레벨업
}
hero.gainExp(100); // 레벨 100 도달, 예외 발생!
} catch (const runtime_error& e) {
cout << "⚠️ " << e.what() << endl;
cout << "✅ 최대 레벨 제한이 올바르게 작동합니다!" << endl;
}
hero.showStatus();
return 0;
}📚 표준 예외 클래스들
C++에서 제공하는 주요 예외 클래스들:
1. logic_error 계열 🧠
#include <stdexcept>
// 논리적 오류 (프로그래밍 실수)
try {
throw invalid_argument("잘못된 매개변수!");
throw out_of_range("배열 범위 초과!");
throw length_error("길이 오류!");
} catch (const logic_error& e) {
cout << "논리 오류: " << e.what() << endl;
}2. runtime_error 계열 ⚡
// 실행 시간 오류 (예측 불가능한 상황)
try {
throw runtime_error("실행 시간 오류!");
throw overflow_error("오버플로우 발생!");
throw underflow_error("언더플로우 발생!");
} catch (const runtime_error& e) {
cout << "실행 오류: " << e.what() << endl;
}3. 시스템 예외들 🖥️
try {
throw bad_alloc(); // 메모리 할당 실패
} catch (const bad_alloc& e) {
cout << "메모리 부족: " << e.what() << endl;
}🎯 실전 예제: 게임 리소스 관리 시스템
파일 로딩과 메모리 할당 예외 처리
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <vector>
using namespace std;
class GameResourceManager {
private:
vector<string> loadedTextures;
vector<int*> allocatedMemory;
public:
~GameResourceManager() {
// 소멸자에서 메모리 정리
for (int* ptr : allocatedMemory) {
delete[] ptr;
}
}
// 텍스처 파일 로딩
void loadTexture(const string& filename) {
if (filename.empty()) {
throw invalid_argument("파일명이 비어있습니다!");
}
ifstream file(filename);
if (!file.is_open()) {
throw runtime_error("파일을 열 수 없습니다: " + filename);
}
loadedTextures.push_back(filename);
cout << "✅ " << filename << " 텍스처 로딩 완료! 🖼️" << endl;
file.close();
}
// 메모리 할당
void allocateGameMemory(int size) {
if (size <= 0) {
throw invalid_argument("메모리 크기는 양수여야 합니다!");
}
if (size > 1000000) { // 1MB 제한
throw length_error("너무 큰 메모리 요청입니다!");
}
try {
int* memory = new int[size];
allocatedMemory.push_back(memory);
cout << "✅ " << size << "개 정수 메모리 할당 완료! 💾" << endl;
} catch (const bad_alloc& e) {
throw runtime_error("메모리 할당에 실패했습니다!");
}
}
// 게임 데이터 유효성 검사
void validatePlayerData(int level, int hp, int mp) {
if (level < 1 || level > 100) {
throw out_of_range("레벨은 1~100 사이여야 합니다!");
}
if (hp < 0) {
throw invalid_argument("HP는 음수가 될 수 없습니다!");
}
if (mp < 0) {
throw invalid_argument("MP는 음수가 될 수 없습니다!");
}
cout << "✅ 플레이어 데이터 검증 완료! 👤" << endl;
}
void showStatus() {
cout << "📊 리소스 상태:" << endl;
cout << " 로딩된 텍스처: " << loadedTextures.size() << "개" << endl;
cout << " 할당된 메모리: " << allocatedMemory.size() << "블록" << endl;
}
};
int main() {
cout << "🎮 게임 리소스 관리 시스템 🎮" << endl;
cout << "================================" << endl;
GameResourceManager resourceMgr;
// 1. 텍스처 로딩 테스트
cout << "🖼️ 텍스처 로딩 테스트:" << endl;
try {
resourceMgr.loadTexture("hero.png");
resourceMgr.loadTexture("background.jpg");
resourceMgr.loadTexture(""); // 예외 발생!
} catch (const invalid_argument& e) {
cout << "❌ 입력 오류: " << e.what() << endl;
} catch (const runtime_error& e) {
cout << "❌ 실행 오류: " << e.what() << endl;
}
cout << endl;
// 2. 메모리 할당 테스트
cout << "💾 메모리 할당 테스트:" << endl;
try {
resourceMgr.allocateGameMemory(1000);
resourceMgr.allocateGameMemory(5000);
resourceMgr.allocateGameMemory(-100); // 예외 발생!
} catch (const invalid_argument& e) {
cout << "❌ 입력 오류: " << e.what() << endl;
} catch (const length_error& e) {
cout << "❌ 크기 오류: " << e.what() << endl;
} catch (const runtime_error& e) {
cout << "❌ 실행 오류: " << e.what() << endl;
}
cout << endl;
// 3. 플레이어 데이터 검증 테스트
cout << "👤 플레이어 데이터 검증 테스트:" << endl;
try {
resourceMgr.validatePlayerData(25, 150, 80); // 정상
resourceMgr.validatePlayerData(101, 100, 50); // 레벨 초과!
} catch (const out_of_range& e) {
cout << "❌ 범위 오류: " << e.what() << endl;
} catch (const invalid_argument& e) {
cout << "❌ 입력 오류: " << e.what() << endl;
}
cout << endl;
// 4. 여러 예외를 한번에 처리
cout << "🎲 종합 테스트 (여러 예외 처리):" << endl;
try {
resourceMgr.validatePlayerData(50, -10, 30); // HP 음수!
resourceMgr.allocateGameMemory(2000000); // 메모리 초과!
} catch (const exception& e) { // 모든 표준 예외의 기본 클래스
cout << "❌ 예외 발생: " << e.what() << endl;
cout << "✅ 안전하게 처리되었습니다!" << endl;
}
cout << endl;
resourceMgr.showStatus();
cout << "🛡️ 모든 예외가 안전하게 처리되었습니다!" << endl;
return 0;
}🔄 예외 전파와 다중 catch
예외 처리의 흐름 이해
#include <iostream>
#include <stdexcept>
using namespace std;
// 함수들이 연쇄적으로 예외를 전파
void level3Function() {
cout << "🔍 Level 3: 깊은 곳에서 문제 발견!" << endl;
throw runtime_error("Level 3에서 오류 발생!");
}
void level2Function() {
cout << "⚡ Level 2: 처리 중..." << endl;
level3Function(); // 예외가 여기로 전파됨
}
void level1Function() {
cout << "🚀 Level 1: 시작..." << endl;
level2Function(); // 예외가 여기로 전파됨
}
int main() {
cout << "🎯 예외 전파 테스트" << endl;
cout << "==================" << endl;
try {
level1Function();
} catch (const runtime_error& e) {
cout << "🛡️ Main에서 예외 포착: " << e.what() << endl;
}
cout << "✅ 프로그램이 정상적으로 계속 실행됩니다!" << endl;
return 0;
}🚨 예외 처리 모범 사례
1. 구체적인 예외부터 처리 📌
try {
// 위험한 코드
} catch (const out_of_range& e) { // 구체적
cout << "범위 오류: " << e.what() << endl;
} catch (const logic_error& e) { // 일반적
cout << "논리 오류: " << e.what() << endl;
} catch (const exception& e) { // 가장 일반적
cout << "기타 오류: " << e.what() << endl;
}2. RAII 원칙 준수 🔄
class GameResource {
public:
GameResource() { cout << "리소스 생성 🔧" << endl; }
~GameResource() { cout << "리소스 정리 🧹" << endl; }
};
void safeFunction() {
GameResource resource; // 자동으로 정리됨
throw runtime_error("오류 발생!"); // 예외가 발생해도 안전
}💡 핵심 정리
- 예외 처리: 프로그램의 예상치 못한 오류를 안전하게 처리하는 메커니즘
- try-catch-throw: 예외 처리의 3요소로 안전한 프로그램 작성
- 표준 예외:
logic_error(논리 오류),runtime_error(실행 오류) 계열 활용 - 예외 전파: 함수 호출 체인을 따라 예외가 상위로 전달
- 구체적 처리: 구체적인 예외부터 일반적인 예외 순으로 catch 블록 배치
- RAII 원칙: 예외 발생 시에도 자원이 안전하게 정리되도록 설계
✅ 실습 체크리스트
🚀 다음 시간 예고
다음 시간에는 사용자 정의 예외에 대해 알아볼 거예요!
- 나만의 예외 클래스 만들기
- 게임별 특화된 예외 처리 시스템
- 예외 클래스 계층 구조 설계
- 맞춤형 오류 메시지와 복구 전략
“예외 처리로 더 안전하고 견고한 프로그램을 만들어보세요! 🛡️✨”
Last updated on