OOP에서의 예외 처리
Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 81번째.
C++에서 예외 처리(Exception handling)는 런타임 오류를 감지하고 대응하기 위한 구조화된 방법을 제공합니다. 객체 지향 프로그래밍에서 예외는 클래스 계층 구조와 자연스럽게 통합되어, 무엇이 잘못되었는지에 대한 문맥을 담고 있는 의미 있는 오류 유형을 생성할 수 있게 해줍니다.
기본 메커니즘은 세 가지 키워드인 try, throw, catch를 사용합니다. 실패할 가능성이 있는 코드는 try 블록에 들어가고, 오류는 throw로 신호를 보내며, catch 블록은 특정 예외 유형을 처리합니다:
#include <iostream>
#include <stdexcept>
class BankAccount {
double balance;
public:
BankAccount(double b) : balance(b) {}
void withdraw(double amount) {
if (amount > balance) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}
double getBalance() const { return balance; }
};
int main() {
BankAccount account(100.0);
try {
account.withdraw(150.0);
} catch (const std::runtime_error& e) {
std::cout << "Error: " << e.what() << "\n";
}
std::cout << "Balance: " << account.getBalance() << "\n";
}C++ 표준 라이브러리는 std::exception을 루트로 하는 예외 클래스 계층 구조를 제공합니다. 일반적인 파생 클래스로는 런타임 문제를 위한 std::runtime_error와 프로그래밍 실수를 위한 std::logic_error가 있습니다. 모든 표준 예외는 오류 메시지를 반환하는 what() 메서드를 제공합니다.
예외가 발생하면 스택 되감기(stack unwinding)가 발생합니다. 프로그램이 일치하는 catch 블록을 찾는 동안 모든 지역 객체에 대해 소멸자가 호출됩니다. 이는 오류가 발생하더라도 적절한 정리를 보장하며, 이것이 RAII와 예외 처리가 매우 잘 어울리는 이유입니다.
챌린지
쉬움객체 지향 컨텍스트에서 예외 처리를 보여주는 안전한 나눗셈 계산기를 만들어 보겠습니다. 나눗셈 연산을 수행하고 문제가 발생했을 때 의미 있는 예외를 던지는 Calculator 클래스를 만든 다음, 메인 프로그램에서 이러한 예외를 우아하게 처리해 볼 것입니다.
코드는 세 개의 파일로 구성됩니다:
Calculator.h: 적절한 에러 처리를 포함하여 수학적 연산을 수행하는Calculator클래스를 정의합니다.Calculator는 나눗셈 결과를 반환하는divide(double numerator, double denominator)메서드를 가져야 합니다. 하지만 분모가 0인 경우,Division by zero라는 메시지와 함께std::runtime_error를 던져야 합니다. 분자나 분모 중 하나라도 음수이면,Negative values not allowed라는 메시지와 함께std::invalid_argument를 던져야 합니다.또한 값의 제곱근을 반환하는
safeSqrt(double value)메서드를 포함하세요. 값이 음수이면,Cannot compute square root of negative number라는 메시지와 함께std::runtime_error를 던지세요.sqrt함수를 사용하려면<cmath>를 포함해야 합니다.Calculator.cpp: 헤더 파일에 선언된 메서드들을 구현합니다. 필요한 헤더(<stdexcept>및<cmath>)를 포함하고 연산을 수행하기 전에 조건을 확인해야 합니다.main.cpp: 세 개의 입력(각각 별도의 줄에 입력됨)을 읽습니다:- 연산 유형:
divide또는sqrt - 첫 번째 숫자 (double)
- 두 번째 숫자 (double) — 나눗셈에만 사용되며, sqrt의 경우 무시됨
Calculator객체를 생성하고 try 블록 안에서 요청된 연산을 수행합니다. 연산이 성공하면Result: [value]를 출력합니다. 예외가 발생하면Error: [message]를 출력하며, 여기서 메시지는 예외의what()메서드에서 가져옵니다.try-catch 블록 이후에(예외 발생 여부와 관계없이), 예외 처리 후에도 프로그램이 계속 실행됨을 보여주기 위해
Calculation complete를 출력합니다.- 연산 유형:
예를 들어, 입력이 divide, 10, 2인 경우:
Result: 5
Calculation complete입력이 divide, 10, 0인 경우:
Error: Division by zero
Calculation complete입력이 divide, -5, 2인 경우:
Error: Negative values not allowed
Calculation complete입력이 sqrt, 16, 0인 경우:
Result: 4
Calculation complete입력이 sqrt, -9, 0인 경우:
Error: Cannot compute square root of negative number
Calculation complete프로그램이 예외를 포착하고 의미 있는 메시지를 표시함으로써 에러를 우아하게 처리한 다음 실행을 계속하는 방식에 주목하세요. 이 패턴은 에러 처리를 깔끔하게 유지하고 메인 로직이 정상적인 흐름(happy path)에 집중할 수 있게 해줍니다.
치트 시트
C++의 예외 처리는 try, throw, catch라는 세 가지 키워드를 사용합니다. 실패할 가능성이 있는 코드는 try 블록에 들어가고, 오류는 throw로 신호를 보내며, catch 블록은 특정 예외 유형을 처리합니다:
try {
// 예외를 발생시킬 수 있는 코드
account.withdraw(150.0);
} catch (const std::runtime_error& e) {
std::cout << "Error: " << e.what() << "\n";
}클래스 메서드에서 예외 발생시키기:
void withdraw(double amount) {
if (amount > balance) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}C++ 표준 라이브러리는 std::exception을 기반으로 하는 예외 클래스들을 제공합니다. 일반적인 유형은 다음과 같습니다:
std::runtime_error- 런타임 문제용std::logic_error- 프로그래밍 실수용std::invalid_argument- 잘못된 인자용
모든 표준 예외는 오류 메시지를 반환하는 what() 메서드를 제공합니다.
예외가 발생하면 스택 해제(stack unwinding)가 일어납니다. 프로그램이 일치하는 catch 블록을 찾는 동안 모든 지역 객체에 대해 소멸자가 호출되어 적절한 정리가 보장됩니다.
표준 예외 클래스를 사용하려면 <stdexcept>를 포함하세요.
직접 해보기
#include <iostream>
#include <string>
#include "Calculator.h"
using namespace std;
int main() {
// 입력 읽기
string operation;
double num1, num2;
cin >> operation;
cin >> num1;
cin >> num2;
// Calculator 객체 생성
Calculator calc;
// TODO: try-catch 블록을 사용하여 연산 수행
// - operation이 "divide"이면, calc.divide(num1, num2)를 호출
// - operation이 "sqrt"이면, calc.safeSqrt(num1)을 호출
// - 성공 시, "Result: "와 그 뒤에 결과를 출력
// - 예외를 catch하고 "Error: "와 그 뒤에 예외 메시지를 출력 (what() 사용)
// - try-catch 이후에, 항상 "Calculation complete"를 출력
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.