icon안동민 개발노트

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) {
    // 이 함수는 예외를 던질 수 있음
}

실습 : 은행 계좌 시스템

 다음 요구사항을 만족하는 간단한 은행 계좌 시스템을 구현해보세요.

  1. InsufficientFundsExceptionInvalidAmountException 예외 클래스 정의
  2. BankAccount 클래스 구현 (입금, 출금, 잔액 조회 기능)
  3. 잔액 부족 시 InsufficientFundsException 발생
  4. 음수 금액 입력 시 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;
}

연습 문제

  1. 템플릿 함수를 사용하여 배열에서 특정 값을 검색하는 함수를 구현하세요. 값을 찾지 못했을 때 예외를 던지도록 합니다.
  2. noexcept 지정자를 사용하여 예외를 던지지 않는 함수를 선언하고, 이 함수 내에서 예외가 발생할 수 있는 다른 함수를 호출할 때 어떻게 처리해야 할지 구현해보세요.

 참고자료