C++ 예외처리(Exception Handling)는 프로그램에서 발생할 수 있는 오류를 관리하고, 프로그램이 예기치 않게 종료되는 것을 방지하는 중요한 기법이다 예외처리는 주로 try-catch 블록을 사용하며, 필요할 경우 throw 키워드를 사용해 명시적으로 예외를 던질 수 있다 또한 throw를 사용할때 발생하는 문제를 해결하기 위한 noexcept 키워드의 예외 명시에 대해서도 알아보면 좋을것 같다( https://learn.microsoft.com/ko-kr/cpp/cpp/errors-and-exception-handling-modern-cpp?view=msvc-170를 참고해보자)
*noexcept를 선호하는 이유와 사용하는 방법
C++에서 예외처리는 try, catch, throw 세 가지 키워드를 기반으로 작동한다
#include <iostream>
using namespace std;
int main() {
try {
throw "예외 발생!";
} catch (const char* e) {
cout << "예외 처리: " << e << endl;
}
return 0;
}
이 코드에서는 throw를 통해 문자열 예외를 던졌고, catch 블록에서 이를 처리하고 있다 결과는 다음과 같다
예외 처리: 예외 발생!
실제 프로그램에서는 문자열보다는 더 구체적인 예외 정보를 전달하기 위해 사용자 정의 예외 클래스를 사용하는 것이 일반적이다 간단한 사용자 정의 예외 클래스를 사용하는 예시를 참고해보자
#include <iostream>
#include <stdexcept>
using namespace std;
class MyException : public exception {
public:
const char* what() const noexcept override {
return "사용자 정의 예외 발생!";
}
};
int main() {
try {
throw MyException();
} catch (const MyException& e) {
cout << e.what() << endl;
}
return 0;
}
이 예제에서는 exception 클래스를 상속받아 사용자 정의 예외 클래스를 만들었으며, what() 함수를 재정의하여 예외 메시지를 제공한다
때로는 다양한 예외 상황을 처리해야 할 때가 있다 이러한 경우 여러 개의 catch 블록을 사용해 예외 유형별로 다른 처리를 할 수 있다
#include <iostream>
#include <stdexcept>
using namespace std;
void divide(int a, int b) {
if (b == 0) throw runtime_error("0으로 나눌 수 없습니다.");
cout << "결과: " << a / b << endl;
}
int main() {
try {
divide(10, 0);
} catch (const runtime_error& e) {
cout << "런타임 오류: " << e.what() << endl;
} catch (...) {
cout << "알 수 없는 예외 발생" << endl;
}
return 0;
}
여기서는 0으로 나누는 상황에서 runtime_error 예외를 발생시키고, 해당 예외를 잡아 처리한다 catch(...) 블록은 모든 예외를 처리할 수 있지만, 예외의 상세 정보를 알 수 없기 때문에 신중하게 사용해야 한다
예외가 발생했을 때도 프로그램의 상태가 안전해야 하며, 자원 누수(resource leak)가 없어야 한다 이를 보장하기 위해 RAII(Resource Acquisition Is Initialization) 패턴과 스마트 포인터를 사용할 수 있다
다음 예제를 보면 예외 발생 시 자원을 안전하게 해제하는 방법을 알수 있다
#include <iostream>
#include <memory>
using namespace std;
class Resource {
public:
Resource() { cout << "자원 획득" << endl; }
~Resource() { cout << "자원 해제" << endl; }
};
void process() {
unique_ptr<Resource> res = make_unique<Resource>();
throw runtime_error("예외 발생");
}
int main() {
try {
process();
} catch (const runtime_error& e) {
cout << "예외 처리: " << e.what() << endl;
}
return 0;
}
이 코드에서 unique_ptr를 사용해 자원을 관리함으로써 예외가 발생해도 자동으로 자원이 해제된다 결과는 다음과 같다
자원 획득
예외 처리: 예외 발생
자원 해제
C++11부터는 함수가 예외를 던지지 않음을 명시하기 위해 noexcept를 사용할 수 있다 예외를 던지지 않는 함수에 noexcept를 붙이면 최적화가 가능하며, 잘못된 예외 처리 흐름을 방지할 수 있다
void safeFunction() noexcept {
// 이 함수는 예외를 던지지 않습니다.
}
만약 noexcept 함수에서 예외가 발생하면, 프로그램은 강제 종료된다 때문에 반드시 예외를 던지지 않을 함수에만 사용해야 한다
C++에서 예외처리는 프로그램 안정성을 높이기 위한 중요한 기법입니다. 특히 실무에서 자원 관리나 오류 처리와 관련된 부분에서 예외처리를 신중히 사용해야 합니다. 적절한 예외 처리 없이 자원이 해제되지 않거나, 오류 상황이 제대로 처리되지 않으면 프로그램이 비정상 종료될 수 있습니다.
throw()는 C++98에서 도입된 구문으로 함수가 예외를 던지지 않는다는 것을 명시하는 방식이다 throw()안에는 함수가 어떤 예외를 던질 수 있는지 명시하는 구문이 있다
void myFunction() throw(int, double); // 이 함수는 int 또는 double 예외만 던질 수 있다고 명시한다
이 방식은 예외를 명시해줌으로써 더 확실해 보이지만 실제로는 이 예외 타입을 컴파일러가 강제해주지 않으며 다른 예외 타입이 나올경우 예외가 전달되지 않은채 프로그램이 강제 종료될수 있다 또한 코드 유지보수가 어려워지며, 예외 타입을 일일이 명시해야 하는 부담이 생긴다
고로 throw() 대신 noexcept등을 통해 예외처리를 보다 명확하고 일관되게 관리하는 것이 오류 처리 방식에서 더 안전하고 효율적인 방법이 되었다
그렇다면 noexcept를 사용할 때 내 함수가 예외가 발생하지 않는 함수인지 어떻게 판단할수 있을까?
기본적으로 함수를 선언할때 noexcept 키워드가 붙어있는 함수를 사용한다면 noexcept 키워드를 붙여 함수를 선언할수 있다 C++ 표준 라이브러리 문서를 통해 noexcept 키워드가 붙은 함수를 확인할수 있지만 IDE(통합 개발 환경)에서는 표준 라이브러리 함수의 문서를 보여주는 기능이 있어 함수 위에 마우스를 올리거나 자동 완성 기능을 사용하면 noexcept 여부를 확인할수 있다(심지어 사용하라고 제안도 해준다)
내 함수에서 예외가 발생하는 여부를 판단할때 예외가 발생하면 종료하는 특성을 가진 noexcept를 붙이고 실행하면 예외의 유무를 확인할수 있다
오늘은 C++의 예외는 어떻게 처리하는지에 대해 알아보았다 마지막으로 알아본 noexcept는 공부하면서 마치 지뢰찾기의 깃발(flag)와 유사하다고 느꼈다 지뢰에 꽂으면 너무 든든하지만 평지에 꽂으면..
어쨋든 이것으로 언리얼 엔진에서 C++로 코드를 작성하는 기초작업은 얼추 끝이 난것 같다 본래 계획대로라면 여기서 파이썬으로 언리얼 엔진을 작동하는 것을 공부해야겠지만 C++에 대해 생각보다 많이 알아버렸으니 다음 시간에는 간단한 언리얼 코드를 몇가지 작성해보려고 한다
레퍼런스
https://learn.microsoft.com/ko-kr/cpp/cpp/errors-and-exception-handling-modern-cpp?view=msvc-170
ChatGPT - 다음은 내가 C++의 예외사항에 대해 공부한 내용이야 내가 앞으로 실무자로써 C++의 예외처리기법을 사용할때 사용해야하는 기법들을 주요 포인트를 잡아서 간단하게 정리하고 추가적으로 참고할 사항이 있는지 정리해줄래?
특별편: 언리얼 엔진에서 C++로 캐릭터 AI 만들기 (2) | 2024.10.19 |
---|---|
C++ 람다 표현식 사용하기(2) (1) | 2024.09.07 |
C++ 람다 표현식 사용하기 (1) | 2024.09.01 |
언리얼 엔진과 C++의 연산자 오버로딩 (0) | 2024.08.17 |
C++이란 어떤 언어일까? (1) | 2024.08.11 |