try-catch 구문
try-catch 구문의 기본 구조
try-catch 구문은 C++에서 예외를 처리하는 핵심 메커니즘입니다.
기본 구조는 다음과 같습니다.
try {
// 예외가 발생할 수 있는 코드
} catch (ExceptionType1 e1) {
// ExceptionType1 타입의 예외 처리
} catch (ExceptionType2 e2) {
// ExceptionType2 타입의 예외 처리
} catch (...) {
// 모든 타입의 예외 처리
}
각 부분의 역할
try
블록 : 예외가 발생할 수 있는 코드를 포함합니다.catch
블록 : 특정 타입의 예외를 처리합니다.catch (...)
: 모든 타입의 예외를 처리하는 catch-all 핸들러입니다.
예외 발생 및 포착
예외는 throw
키워드를 사용하여 발생시킬 수 있습니다.
발생한 예외는 적절한 catch
블록에서 포착됩니다.
#include <iostream>
#include <stdexcept>
int divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Division by zero");
}
return a / b;
}
int main() {
try {
std::cout << divide(10, 2) << std::endl; // 정상 실행
std::cout << divide(10, 0) << std::endl; // 예외 발생
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
return 0;
}
이 예제에서 divide(10, 0)
은 예외를 발생시키고, 이는 catch
블록에서 포착됩니다.
다중 catch 블록
여러 타입의 예외를 처리하기 위해 다중 catch
블록을 사용할 수 있습니다.
#include <iostream>
#include <stdexcept>
void riskyFunction(int x) {
if (x < 0) {
throw std::runtime_error("Negative value");
} else if (x == 0) {
throw std::logic_error("Zero value");
}
}
int main() {
try {
riskyFunction(-1);
} catch (const std::runtime_error& e) {
std::cerr << "Runtime error: " << e.what() << std::endl;
} catch (const std::logic_error& e) {
std::cerr << "Logic error: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Unknown exception" << std::endl;
}
return 0;
}
catch 블록의 순서는 중요합니다. 더 구체적인 예외 타입을 먼저 처리해야 합니다.
예외 객체
catch 블록에서 예외 객체를 참조로 받아 처리할 수 있습니다.
이를 통해 예외에 대한 추가 정보를 얻을 수 있습니다.
class MyException : public std::exception {
private:
std::string message;
public:
MyException(const std::string& msg) : message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
try {
throw MyException("Custom exception occurred");
} catch (const MyException& e) {
std::cerr << "Caught MyException: " << e.what() << std::endl;
}
예외 재전파
catch 블록에서 예외를 다시 throw할 수 있습니다.
이를 통해 부분적인 처리 후 상위 호출자에게 예외를 전달할 수 있습니다.
void function1() {
try {
// 예외 발생 가능 코드
throw std::runtime_error("Error in function1");
} catch (const std::exception& e) {
std::cerr << "Caught in function1: " << e.what() << std::endl;
throw; // 예외 재전파
}
}
void function2() {
try {
function1();
} catch (const std::exception& e) {
std::cerr << "Caught in function2: " << e.what() << std::endl;
}
}
중첩된 try-catch
try-catch 블록은 중첩될 수 있습니다.
내부 블록에서 처리되지 않은 예외는 외부 블록으로 전파됩니다.
try {
try {
throw std::runtime_error("Inner exception");
} catch (const std::logic_error& e) {
// 이 catch 블록은 실행되지 않음
std::cerr << "Inner catch: " << e.what() << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "Outer catch: " << e.what() << std::endl;
}
함수 try 블록
생성자의 초기화 리스트에서 발생하는 예외를 잡기 위해 함수 try 블록을 사용할 수 있습니다.
class MyClass {
int* data;
public:
MyClass(int size) try : data(new int[size]) {
// 생성자 본문
} catch (const std::bad_alloc& e) {
std::cerr << "Allocation failed: " << e.what() << std::endl;
throw;
}
};
noexcept 지정자
함수가 예외를 던지지 않음을 명시하기 위해 noexcept
지정자를 사용할 수 있습니다.
void safeFunction() noexcept {
// 이 함수는 예외를 던지지 않음을 보장
}
void potentiallyUnsafeFunction() noexcept(false) {
// 이 함수는 예외를 던질 수 있음
}
실습 : 은행 계좌 시스템
다음 요구사항을 만족하는 간단한 은행 계좌 시스템을 구현해보세요.
InsufficientFundsException
과InvalidAmountException
예외 클래스 정의BankAccount
클래스 구현 (입금, 출금, 잔액 조회 기능)- 잔액 부족 시
InsufficientFundsException
발생 - 음수 금액 입력 시
InvalidAmountException
발생
#include <iostream>
#include <stdexcept>
#include <string>
class InsufficientFundsException : public std::runtime_error {
public:
InsufficientFundsException(const std::string& msg) : std::runtime_error(msg) {}
};
class InvalidAmountException : public std::runtime_error {
public:
InvalidAmountException(const std::string& msg) : std::runtime_error(msg) {}
};
class BankAccount {
private:
double balance;
std::string accountNumber;
public:
BankAccount(const std::string& accNum, double initialBalance)
: accountNumber(accNum), balance(initialBalance) {
if (initialBalance < 0) {
throw InvalidAmountException("Initial balance cannot be negative");
}
}
void deposit(double amount) {
if (amount < 0) {
throw InvalidAmountException("Deposit amount cannot be negative");
}
balance += amount;
}
void withdraw(double amount) {
if (amount < 0) {
throw InvalidAmountException("Withdrawal amount cannot be negative");
}
if (amount > balance) {
throw InsufficientFundsException("Insufficient funds for withdrawal");
}
balance -= amount;
}
double getBalance() const { return balance; }
std::string getAccountNumber() const { return accountNumber; }
};
int main() {
try {
BankAccount account("1234567890", 1000);
std::cout << "Initial balance: " << account.getBalance() << std::endl;
try {
account.deposit(500);
std::cout << "After deposit: " << account.getBalance() << std::endl;
account.withdraw(200);
std::cout << "After withdrawal: " << account.getBalance() << std::endl;
account.withdraw(2000); // This should throw an exception
} catch (const InsufficientFundsException& e) {
std::cerr << "Transaction failed: " << e.what() << std::endl;
}
account.deposit(-100); // This should throw an exception
} catch (const InvalidAmountException& e) {
std::cerr << "Invalid operation: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Unexpected error: " << e.what() << std::endl;
}
return 0;
}
연습 문제
- 템플릿 함수를 사용하여 배열에서 특정 값을 검색하는 함수를 구현하세요. 값을 찾지 못했을 때 예외를 던지도록 합니다.
noexcept
지정자를 사용하여 예외를 던지지 않는 함수를 선언하고, 이 함수 내에서 예외가 발생할 수 있는 다른 함수를 호출할 때 어떻게 처리해야 할지 구현해보세요.
참고자료
- try-catch 블록 : try-catch
- 예외 안전성에 대한 설명 : Exception safety
- 함수 try 블록 : Function try block
- noexcept 지정자 : noexcept specifier
- C++ Core Guidelines의 예외 처리 관련 항목 : E : Error handling