MSDN
Try, Throw 및 Catch 문(C++) : https://msdn.microsoft.com/ko-kr/library/6dekhbbc.aspx
<stdexcept> : https://msdn.microsoft.com/ko-kr/library/t65b74ad.aspx
예외 처리는 일반적인 실행의 흐름을 바꾸는 몇 가지 조건을 처리하도록 설계한 프로그래밍 언어의 개념이나 컴퓨터 하드웨어 구조를 말한다.
사용 키워드는 try, catch, throw를 사용한다.
try : 예외 발생을 체크할 구문
catch : 예외 발생시 처리할 구문
throw : 예외 발생시키는 구문
#include <stdexcept> // 익셉션 클래스가 정의되어 있는 헤더
MyData md;
try
{
// Code that could throw an exception
md = GetNetworkResource();
}
catch (const networkIOException& e)
{
// try 블록에서 networkIOException 형식의 예외가 발생할 때 실행되는 코드
// 예외 객체에 오류 메시지를 기록하십시오
cerr << e.what() << endl;
}
catch (const myDataFormatException& e)
{
// 다른 예외 유형을 처리하는 코드
cerr << "error" << endl;
}
catch (...)
{
// 그 외의 모든 예외 유형을 처리
cerr << e.what();
}
// 다음 구문은 throw 식을 보여줍니다.
MyData GetNetworkResource()
{
// ...
if (IOSuccess == false)
throw networkIOException("Unable to connect");
// ...
if (readError)
throw myDataFormatException("Format error");
// ...
}
익셉션으로 던질 데이터는 어떤 타입이든 사용할 수 있다.
꼭 객체를 이용할 필요없이 int, char*도 가능하다.
ex)
throw "Not open the file"
catch(const char* e) { cerr << e << endl; }
다만 다음과 같은 이유로 객체를 사용하는 것이 좋다.
객체는 클래스 이름에 중요한 정보가 전달된다
객체는 여러 종류의 데이터를 전달할 수 있다
그리고 객체로 받을 땐 const 참조 형식으로 받는 것을 권장한다.
익셉션 클래스가 계층을 이루고 있기 때문에 익셉션을 처리할 때 다형성에 의존할 수 있다.
invalid_argument와 runtime_argument는 exception의 서브클래스 이기 때문에 두개의 catch 구문을 exception 타입을 받도록 하여 하나로 합칠 수 있다.
다만 참조로 받지 않으면 슬라이싱이 발생해서 하위 클래스가 가진 세부 정보를 잃어버린다.
catch(const invalid_argument& e)
{
}
catch(const runtime_argument& e)
{
}
=====>
catch(const exception& e)
{
}
커스텀 익셉션을 만들면 특정한 상황에 맞추어진 의미 있는 이름을 사용할 수 있고, 기본 타입의 에러 메시지 외에 필요한 정보를 추가로 담을 수 있다.
(대신에 표준 exception을 상속받는 것을 추천한다)
class FileError : public runtime_exception
{
public:
FileError(const string& fileIn) : runtime_error(""), mFile(fileIn) {}
virtual const char* what() const noexcept { return mMsg.c_str(); }
string getFileName() { return mFile; }
protected:
string mFile, mMsg;
};
class FileOpenError : public FileError
{
public:
FileOpenError(const string& fileNameIn);
};
FileOpenError::FileOpenError(const string& fileNameIn) : FileError(fileNameIn)
{
mMsg = "Unable to open " + fileNameIn;
}
인셉션을 처리하는 과정에서 인셉션이 발생하는 경우 기존 정보가 사라져 원인이 뭔지 알 수 없기 때문에
C++11에서는 중첩된 익셉션(nested_exception)을 제공한다.
class MyException : public std::exception, public std::nested_exception
{
public:
MyException(const char* msg) : mMsg(msg) {}
virtual ~MyExcpetion() noexcept() {}
virtual const char* what() const noexcept { return mMsg.c_str(); }
protected:
std::string mMsg;
};
void doSomething()
{
try
{
throw std::runtime_error("Throw - runtime_error");
}
catch (const std::runtime_error& e)
{
std::cout << __func__ << "caught a runtime_error" << std::endl;
std::cout << __func__ << "throw MyException" << std::endl;
std::throw_with_nested(
MyException("MyException with nested runtime_error"));
}
}
int main()
{
try
{
dsSomething();
}
catch (const MyException& e)
{
std::cout << __func__ << "caught MyException:" << e.waht() << std::endl;
#ifdef v1
const std::nested_exception* pNestedd = dynamic_cast<const std::nested_exception*>(&e);
if (pNested)
{
try
{
pNested->rethrow_nested();
}
catch (const std::runtime_error& e)
{
std::cout << "Nested exception:" << e.what() << std::endl;
}
}
#else
try
{
std::rethorw_if_nested(e);
}
catch (const std::runtime_error& e)
{
std::cout << "Nested exception:" << e.what() << std::endl;
}
#endif
}
return 0;
}
어떤 코드에서 익셉션을 발생시키면 캐치 핸들러를 찾기위해 몇 단계의 호출 스택을 거슬러 올라갈 수 있다.
스택 되돌림이 일어날 때 동적할당된 메모리는 해제되지 않기 때문에 메모리 릭이 일어날 수 있다.
다음과 같이 여러가지 방법으로 해결할 수 있다.
1. 스마트 포인터
#include <memory>
using namespace std;
void funcOne() throw(exception)
{
string std1;
unique_ptr<string> str2(new string("hello"));
funcTow();
}
2. 인셉션 받기, 리소스 정리, 재전송
void funcOne() throw(exception)
{
string str1;
string* str2 = new string();
try
{
funcTwo();
}
catch(...)
{
delete str2;
throw; // 익셉션 재전송
}
delete str2;
}
메모리 할당 에러시 처리
try
{
ptr = new int[numInts];
}
catch (const bad_alloc& e)
{
cerr << __FILE__ << "(" << __LINE__ << "): Unable to allocate memory!" << endl;
return;
}
'프로그래밍 > C,C++' 카테고리의 다른 글
람다(lambda) (0) | 2018.04.11 |
---|---|
STL 알고리즘 (0) | 2018.04.02 |
STL 컨테이너 (0) | 2018.04.02 |
포인터 인자 검증하기 (0) | 2016.07.20 |
[C]Split함수 구현 (0) | 2016.04.20 |