728x90
반응형
Study about exception handling
- try {} ~ catch() 문을 통한 구조화된 예외처리가 있음
- try내 throw 가 나오면 객체로 떼어서 생각 구조화된 예외처리를 잘하자
함수속에서 throw 할 경우
void ExceptTest1()
{
int a;
if(!a)
{
throw a;
}
}
void ExceptTest2()
{
char ch;
if(!ch)
{
throw ch;
}
}
int main(void)
{
try
{
ExceptTest1();
ExceptTest2();
}
catch (int test1)
{
std::cout<<"ExceptTest1() Error" << std::endl;
}
catch (char test2)
{
std::cout<<"ExceptTest2() Error" << std::endl;
}
}
- try 내에선 정상적으로 돌아만가고 catch에선 함수속에서 실패했을때 throw를 던져도 알맞게 작동한다.
- 즉 구조화를 잘해놓으면 개별화된게 눈에 잘 보여 코드 가독성이 좋아짐
- 에러처리를 분류하는데 다중 if문이나 switch-case를 안써도 에러처리 가능해짐 (goto문으로 에러처리하던걸 찾아보면 이해쉬움)
예외 클래스
class MyException
{
public:
MyException(int nCode, const char *psz)
{
m_nErrorCode = nCode;
strcpy_s(m_sz, sizeof(m_sz), psz);
}
int GetErrorCode() const { return m_nErrorCode; }
const char* GetMessager() const { return m_sz; }
private:
int m_nErrorCode;
char m_sz[128];
};
int main(void)
{
try
{
int nInput(0);
std::cout << "양의 정수 입력" << std::endl;
std::cin >> nInput;
if(nInput < 0)
{
MyException exp(10, "양의 정수를 입력해야합니다");
throw exp
}
}
catch(MyExceptoin &exp)
{
std::cout << "Error Code {" <<exp.GetErrorCode() << "}" << exp.GetMessager() << std::endl;
}
return 0;
}
- thorw 던지는 구문을 main 바깥 함수로 작성하여 처리해도 가능하다.
- 이과정에서 좋은점은 함수가 함수를 부르면 stack unwinding이 발생해 스택프레임이 여러개가 올라간다
stack unwinding 예제
void TestFunc1()
{
// 가장 마지막에 호출된 함수가 예외를 던진다.
std::cout << "***TestFunc1() - Begin***"<< std::endl;
//throw 0;
std::cout << "***TestFunc1() - End***"<< std::endl;
}
void TestFunc2()
{
std::cout << "***TestFunc2() - Begin***"<< std::endl;
TestFunc1();
std::cout << "***TestFunc2() - End***"<< std::endl;
}
void TestFunc3()
{
std::cout << "***TestFunc3() - Begin***"<< std::endl;
TestFunc2();
std::cout << "***TestFunc3() - End***"<< std::endl;
}
int main(void)
{
try
{
TestFunc3();
}
catch(...)
{
// 함수들을 반환하지않고 스택이 즉시 풀려 흐림이 넘어온다.
std::cout << "Exception handling"<< std::endl;
}
return 0;
}
- main 위에 TestFunc3() -> TestFunc2() -> TestFunc1() 순으로 부르다면서 스택이 쌓인다 (부른순서대로 main위에 쌓임)
- 함수를 반환하면서 맨 위부터 즉 TestFunc1() -> TestFunc2() -> TestFunc3() 순으로 스택을 풀어나간다
- 그러나 TestFunc1()에서 예외를 던지면 함수반환없이 스택을 한번에 풀어준다. (매우 큰 장점)
stack unwinding 장점 (메모리 동적할당시)
- C에서 malloc() 은 함수
- C++에서 new 는 연산자다
- 함수는 반환값을보고 메모리할당이 성공인지 실패인지 따질수있으나 연산자는 실패를 고려하겠다는 개념이아니다
- 아래 예제를 통해 구조화된 예외처리방법과 장점 확인
class Test
{
public:
Test(int nSize)
{
//메모리 할당 실패시 예외를 던진다
m_pszData = new char[nSize];
}
~Test()
{
delete[] m_pszData;
std::cout << "정상적으로 객체 소멸" << std::endl;
}
private:
char *m_pszData;
};
int main(void)
{
try
{
int nSize(0);
std::cout << "Input size" << std::endl;
std::cin >> nSize;
Test test1(nSzie); // 여기서 객체 메모리 동적할당
}
catch(bad_alloc &exp)
{
//시스템이 밝힌 원인을 찾는다
std::cout << exp.what() << std::endl;
std::cout << "ErrorL: Test()" << std::endl;
}
return 0;
}
- 만약 bad_alloc 클래스가 객체 메모리할당에서 실패시 예외를 던져주는 역할을할때 현재 main()함수의 try에서 객체 메모리 동적할당을 했는데 if문을 사용하지 않더라도 실패시 바로 catch() 문으로 간다.
- 생성자의 경우 반환값이없어 객체생성이 실패인지 성공인지 모르고 생성자의 호출시점을 내가 정할수없기에 이를 확인할때 매우 유용하다.
728x90
반응형
'C++ 공부' 카테고리의 다른 글
댕글링 포인터 (Dangling Pointer) (0) | 2023.06.19 |
---|---|
lambda (0) | 2021.08.22 |
template (0) | 2021.08.20 |
relational (0) | 2021.08.20 |
inheritance 04 (0) | 2021.08.20 |