프로그래밍/C,C++

예외처리(exception)

MAKGA 2018. 4. 1. 18:54
320x100

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;

}


320x100

'프로그래밍 > 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