C++ 예외 처리, 상수

윤준혁·2025년 3월 2일

예외 처리(Exception Handling)

예외 처리란?

  • 프로그램이 실행되는 동안 발생할 수 있는 오류(예외)를 감지하고, 이를 적절히 처리하여 프로그램이 비정상적으로 종료되지 않도록 하는 기능
  • 프로그램의 안정성과 유지보수성을 높이는 데 필수적인 요소이며, try, catch, throw 키워드를 사용하여 구현

오류의 종류

문법 오류(Syntax Error)

  • 잘못된 문법으로 인해 컴파일이 불가능한 오류
  • 컴파일러가 오류를 감지하고 알맞게 수정

논리 오류(Logical Error)

  • 문법적으로는 올바르지만, 의도한 결과와 다르게 동작하는 오류
  • 디버거를 사용하여 오류를 분석하고 수정

실행 시간 오류(Runtime Error)

  • 프로그램 실행 중 발생하는 오류로, 주로 잘못된 입력 또는 리소스 문제가 원인
  • 예외 처리르 통해 적절한 대처 필요

예외 처리 구문

  • try, catch, throw를 사용하여 예외를 처리한다

기본 구조

try {
    // 예외가 발생할 가능성이 있는 코드
} catch (예외 타입 변수) {
    // 예외 처리 코드
}
#include <iostream>
using namespace std;

int divide(int a, int b) {
    if (b == 0) {
        throw "Division by zero error!"; // 예외 발생
    }
    return a / b;
}

int main() {
    try {
        cout << divide(10, 0) << endl;
    } catch (const char* msg) {
        cerr << "Caught exception: " << msg << endl;
    }
    return 0;
}

출력 예)
Caught exception: Division by zero error!

예외 처리 기법

여러개의 catch 블록

  • 여러 타입의 예외를 개별적으로 처리할 수 있다
try {
    throw 42;
} catch (int e) {
    cout << "Caught an integer: " << e << endl;
} catch (double e) {
    cout << "Caught a double: " << e << endl;
} catch (const char* e) {
    cout << "Caught a string: " << e << endl;
}

catch(...) - 모든 예외 잡기

  • 어떤 유형의 예외가 발생할지 모를 경우 모든 예외를 처리할 수 있도록 catch(...)을 사용
try {
    throw 3.14;
} catch (...) {
    cout << "Caught an unknown exception" << endl;
}

사용자 정의 예외 클래스

  • 사용자 정의 예외 클래스를 만들어 보다 구체적인 예외 처리를 수행할 수 있다
#include <iostream>
#include <stdexcept>
using namespace std;

class MyException : public exception {
public:
    const char* what() const noexcept override {
        return "Custom exception occurred!";
    }
};

int main() {
    try {
        throw MyException();
    } catch (const MyException& e) {
        cout << e.what() << endl;
    }
    return 0;
}

출력 예)
Custom exception occurred!

주의할 점

예외 남발 금지

  • 모든 오류를 예외로 처리할 필요는 없다
  • 정상적인 흐름을 예외로 처리하면 코드가 복잡해질 수 있다

리소스 정리 필요

  • 예외가 발생하면 객체가 제대로 해제되지 않을 수 있으므로, RAII(Resource Acquisition Is Initialization) 패턴을 활용하는 것이 좋다

표준 라이브러리 활용

  • C++의 표준 라이브러리에는 std::exception, std::runtime_error 등의 예외 클래스를 제공하므로 이를 활용하는 것이 좋다

상수(Contrant)

상수란?

  • 값을 변경할 수 없는 변수를 의미, 즉, 프로그램 실행 도중 값을 바꿀 수 없는 데이터를 나타낸다

상수의 종류

리터럴 상수(Literal Constants)

  • 소스 코드 내에 직접 작성된 상수 값을 의미
  • 리터럴 자체는 변경할 수 없으며, 특정 변수에 할당될 때 변수가 변경될 수 있는지 여부는 변수의 선언 방식에 따라 달라진다
int x = 10;   // 정수 리터럴
char ch = 'A'; // 문자 리터럴
double pi = 3.14; // 실수 리터럴

const 키워드를 사용한 상수(Contant Variables)

  • const 키워드를 사용하면 변수를 선언할 때 값을 한 번만 설정하고 변경할 수 없도록 만들 수 있다
const int MAX_USERS = 100;
MAX_USERS = 200; // 오류! 상수는 변경 불가능

constexpr 키워드를 사용한 상수(Constant Expressions)

  • constexpr은 컴파일 시간에 값을 결정할 수 있는 상수를 의미한다
constexpr int square(int x) { return x * x; }
constexpr int result = square(5); // 컴파일 타임에서 계산됨
  • const는 런타임에도 값을 결정할 수 있다
  • constexpr은 반드시 컴파일 타임에 계산 가능해야 한다

매크로 상수(#define)

  • C에서 많이 사용되었던 #define을 이용한 매크로 방식도 있지만, const 또는 constexpr을 사용하는 것이 더 안전하다
  • #define 방식은 타입 안정성이 없고 디버깅이 어려우므로, C++에서는 const 또는 constexpr을 사용하는 것을 권장
#define PI 3.141592
std::cout << PI; // 3.141592

상수 한정자(Constant Qualifier)

  • 상수 한정자를 사용하여 변수, 포인터, 함수, 매개변수 등에 대해 변경 불가능한 속성을 지정할 수 있다

const 포인터

int a = 10, b = 20;
const int* p1 = &a; // p1이 가리키는 값을 변경할 수 없음
int* const p2 = &a; // p2가 다른 주소를 가리킬 수 없음
const int* const p3 = &a; // p3는 값도 변경 불가능, 주소도 변경 불가능

const 함수 매개변수

void printValue(const int x) {
    // x = 100; // 오류 발생! x는 변경 불가
    std::cout << x << std::endl;
}

const 멤버 함수

class MyClass {
public:
    void showValue() const { // 이 함수는 멤버 변수를 변경할 수 없음
        std::cout << "This is a constant function" << std::endl;
    }
};

전처리기(Preprocessor)

  • 컴파일 전에 실행되는 명령어이다
  • #로 시작하는 지시문들을 사용하여 코드의 특정 부분을 조작할 수 있다

주요 명령어

  • #define : 매크로 정의
  • #include : 헤더 파일 포함
  • #ifdef, #ifndef : 조건부 컴파일
  • #pragma : 컴파일러에 특정 지시
#include <iostream>
#include "myheader.h" // 사용자 정의 헤더 포함

#ifdef DEBUG
    std::cout << "디버그 모드 활성화";
#endif

네임스페이스(Namespaces)

  • 이름 충돌 방지를 위한 C++의 기능
  • using namespace std;를 남용하면 네임스페이스 충돌 위험이 있으므로 주의
namespace First {
    int value = 10;
}

namespace Second {
    int value = 20;
}

std::cout << First::value; // 10
std::cout << Second::value; // 20
using namespace std;
std::cout << "Hello, World!";

0개의 댓글