C++ 초급 - 10. 예외 처리 (3 - 기본 예외 클래스 (std::exception, std::runtime_error))

2025. 2. 22. 17:23프로그래밍 언어/C++

📌 10.3 기본 예외 클래스 (std::exception, std::runtime_error)

C++ 표준 라이브러리는 예외 처리를 위한 다양한 표준 예외 클래스(std::exception 계열)를 제공한다.
이를 활용하면 예외를 일관된 방식으로 처리하고, 예외 발생 원인을 보다 쉽게 분석할 수 있다.


📌 1. 표준 예외 클래스 (std::exception, std::runtime_error, std::logic_error 등)의 개념

🔹 (1) 표준 예외 클래스란?

  • C++ 표준 라이브러리는 std::exception을 기반으로 다양한 예외 클래스를 제공.
  • 모든 표준 예외 클래스는 std::exception을 상속받음.
  • 예외 발생 시 what() 멤버 함수를 사용하여 예외 메시지를 제공.

💡 표준 예외 클래스 계층 구조

std::exception  <-- C++ 모든 표준 예외 클래스의 기본 클래스
 ├── std::logic_error    (논리적 오류, 실행 전에 감지 가능)
 │    ├── std::invalid_argument    (잘못된 함수 인자)
 │    ├── std::domain_error        (잘못된 수학적 연산)
 │    ├── std::length_error        (컨테이너 크기 초과)
 │    ├── std::out_of_range        (잘못된 인덱스 접근)
 │
 ├── std::runtime_error  (런타임 오류, 실행 중 감지)
 │    ├── std::range_error        (수치 범위 초과)
 │    ├── std::overflow_error     (산술 오버플로우)
 │    ├── std::underflow_error    (산술 언더플로우)
 │    ├── std::system_error       (시스템 관련 오류)
 │
 ├── std::bad_alloc       (메모리 할당 실패)
 ├── std::bad_cast        (잘못된 타입 변환)
 ├── std::bad_typeid      (잘못된 typeid 접근)

💡 예제: 표준 예외 클래스 기본 사용법

#include <iostream>
#include <stdexcept>  // 표준 예외 클래스 헤더 포함

int main() {
    try {
        throw std::runtime_error("런타임 오류 발생!");
    }
    catch (const std::exception& e) {
        std::cout << "예외 처리: " << e.what() << std::endl;
    }
    return 0;
}

🔹 출력 결과

예외 처리: 런타임 오류 발생!

💡 설명

  • std::runtime_error("런타임 오류 발생!")을 throw하여 예외 발생.
  • catch (const std::exception& e)에서 what()을 사용하여 오류 메시지 출력.

📌 2. what() 함수를 활용한 예외 메시지 출력

🔹 (1) std::exception::what()의 역할

  • 모든 표준 예외 클래스는 what() 함수를 제공하여 예외 메시지를 문자열(const char*)로 반환.
  • 예외 발생 원인을 쉽게 확인 가능.

💡 예제: what() 함수 활용

#include <iostream>
#include <stdexcept>

int main() {
    try {
        throw std::invalid_argument("잘못된 인자 전달!");
    }
    catch (const std::exception& e) {
        std::cout << "예외 발생: " << e.what() << std::endl;
    }
    return 0;
}

🔹 출력 결과

예외 발생: 잘못된 인자 전달!

💡 설명

  • std::invalid_argument를 사용하여 잘못된 함수 인자가 전달되었음을 알림.
  • what() 함수를 사용하면 예외 원인을 쉽게 파악 가능.

📌 3. 사용자 정의 예외 클래스를 std::exception에서 상속하여 만들기

🔹 (1) std::exception을 상속하여 새로운 예외 클래스 생성 가능

  • 사용자 정의 예외를 만들 때 std::exception을 상속받아 what()을 오버라이딩하면 예외 메시지 제공 가능.

💡 예제: 사용자 정의 예외 클래스

#include <iostream>
#include <exception>

// 사용자 정의 예외 클래스
class MyException : public std::exception {
public:
    const char* what() const noexcept override {
        return "사용자 정의 예외 발생!";
    }
};

int main() {
    try {
        throw MyException();
    }
    catch (const std::exception& e) {
        std::cout << "예외 처리: " << e.what() << std::endl;
    }
    return 0;
}

🔹 출력 결과

예외 처리: 사용자 정의 예외 발생!

💡 설명

  • MyException 클래스는 std::exception을 상속받아 사용자 정의 예외를 생성.
  • what()을 오버라이딩하여 사용자 정의 예외 메시지를 제공.

📌 4. std::bad_alloc을 활용한 메모리 할당 예외 처리

🔹 (1) std::bad_alloc 예외

  • std::bad_alloc은 메모리 할당(new)이 실패했을 때 발생하는 예외.

💡 예제: 메모리 부족으로 std::bad_alloc 예외 발생

#include <iostream>
#include <new>  // std::bad_alloc 포함

int main() {
    try {
        // 매우 큰 배열을 할당하여 메모리 부족 발생
        int* largeArray = new int[100000000000];
        delete[] largeArray;
    }
    catch (const std::bad_alloc& e) {
        std::cout << "메모리 할당 실패: " << e.what() << std::endl;
    }
    return 0;
}

🔹 출력 결과

메모리 할당 실패: std::bad_alloc

💡 설명

  • 너무 큰 메모리를 할당하려 하면 std::bad_alloc 예외가 발생.
  • catch (const std::bad_alloc& e)에서 예외를 처리하고 what()으로 메시지 출력.

📌 5. 라이브러리 함수(std::stoi, std::vector::at 등)에서 발생하는 표준 예외 처리 방법

🔹 (1) std::stoi의 std::invalid_argument 예외

💡 예제: 잘못된 문자열을 정수로 변환할 때 예외 발생

#include <iostream>
#include <string>

int main() {
    try {
        int num = std::stoi("abc");  // 잘못된 문자열
    }
    catch (const std::invalid_argument& e) {
        std::cout << "예외 발생: " << e.what() << std::endl;
    }
    return 0;
}

🔹 출력 결과

예외 발생: stoi

💡 설명

  • "abc"는 정수로 변환할 수 없으므로 std::invalid_argument 예외가 발생.

🔹 (2) std::vector::at의 std::out_of_range 예외

💡 예제: 벡터의 범위를 벗어난 인덱스 접근 시 예외 발생

#include <iostream>
#include <vector>

int main() {
    try {
        std::vector<int> vec = {1, 2, 3};
        std::cout << vec.at(10) << std::endl;  // 존재하지 않는 인덱스 접근
    }
    catch (const std::out_of_range& e) {
        std::cout << "예외 발생: " << e.what() << std::endl;
    }
    return 0;
}

🔹 출력 결과

예외 발생: vector::_M_range_check: __n (which is 10) >= this->size() (which is 3)

💡 설명

  • vec.at(10)을 호출하면 벡터 크기보다 큰 인덱스 접근으로 std::out_of_range 예외 발생.

📌 6. 정리

개념  설명
표준 예외 클래스 std::exception을 기반으로 다양한 예외 클래스 제공
what() 함수 예외 메시지를 반환하여 오류 원인 분석 가능
사용자 정의 예외 std::exception을 상속하여 커스텀 예외 클래스 생성 가능
std::bad_alloc 예외 메모리 할당 실패 시 발생
라이브러리 예외 처리 std::stoi, std::vector::at 등의 표준 예외 활용 가능