C++ Chapter 14.3 : 예외 클래스와 상속
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!
chapter 14. 예외처리 : 예외 클래스와 상속
🔔 throw시 기본 자료형 사용
#include <iostream>
class MyArray
{
private:
int m_data[5];
public:
int & operator [] (const int & index)
{
if (index < 0 || index >= 5) throw -1;
return m_data[index];
}
};
void doSomething()
{
MyArray my_array;
try
{
my_array[100];
}
catch (const int & x)
{
cerr << "Exception " << x << endl;
}
}
int main()
{
doSomething();
cout << "main" << endl;
return 0;
}
💎출력💎
Exception -1
- doSomething 함수의
try
의my_array[100]
- 👉
[]
연산자 오버로딩 호출- index가 5 이상이므로 예외 발생
throw -1
- return m_data[index]는 실행되지 않는다.
- index가 5 이상이므로 예외 발생
- 👉
catch (const int & x)
에서 에러 메세지-1
을 받는다.- “Exception -1” 출력
- 👉
이렇게
int
같은 기본 자료형 타입의 데이터를throw
하면 어떤 예외인지를 표현하는데 한계가 있다.
🔔 throw시 예외 클래스 객체 사용
throw
시 예외 클래스를 사용하여 어떤 예외인지를 표현한다.
#include <iostream>
class Exception
{
public:
void report()
{
cerr << "Exception report" << endl;
}
};
class MyArray
{
private:
int m_data[5];
public:
int & operator [] (const int & index)
{
if (index < 0 || index >= 5) throw Exception();
return m_data[index];
}
};
void doSomething()
{
MyArray my_array;
try
{
my_array[100];
}
catch (const int & x)
{
cerr << "Exception " << x << endl;
}
catch (Exception & e)
{
e.report();
}
}
int main()
{
doSomething();
cout << "main" << endl;
return 0;
}
💎출력💎
Exception report
- 예외 클래스 Exception을 정의 해주었다.
throw Exception();
- 예외 클래스의 '익명 객체'를 생성한 후 이를 던진다.
- throw ArrayException 이런식으로 클래스 이름을 정해 주면 어떤 종류의 예외를 발생시켰는지 한 눈에 알 수 있다!
catch (Exception & e)
에서 이를 받는다.-
Exception & e = Exception();
e.report()
- 예외 클래스 객체의 멤버 함수인 report가 실행된다.
- 즉, 얘가 예외 처리를 함
- “Exception report” 출력
-
🔔 예외 클래스의 상속
Exception
클래스를 상속 받는ArrayException
클래스를 만들어 보자.
객체 잘림 현상
#include <iostream>
class Exception
{
public:
void report()
{
cerr << "Exception report" << endl;
}
};
class ArrayException : public Exception
{
public:
void report() // 오버라이딩
{
cerr << "Array Exception report" << endl;
}
}
class MyArray
{
private:
int m_data[5];
public:
int & operator [] (const int & index)
{
if (index < 0 || index >= 5) throw ArrayException();
return m_data[index];
}
};
void doSomething()
{
MyArray my_array;
try
{
my_array[100];
}
catch (const int & x)
{
cerr << "Exception " << x << endl;
}
catch (Exception & e)
{
e.report();
}
}
int main()
{
doSomething();
cout << "main" << endl;
return 0;
}
💎출력💎
Exception report
객체 잘림 현상 때문에
throw ArrayException()
하더라도ArrayException
클래스가 아닌Exception
클래스의 report 함수가 실행된다.
- Exception 클래스를 상속 받는 또 다른 예외 클래스 ArrayException을 정의 해주었다.
throw ArrayException();
- 예외 클래스의 '익명 객체'를 생성한 후 이를 던진다.
- 배열 예외가 발생했구나 하고 한눈에 딱 알 수 있음!
catch (Exception & e)
에서 이를 받는다.-
Exception & e = ArrayException();
- 그러나 부모인 Exception 타입의 참조 변수로 받고 있기 때문에 객체 잘림 현상이 일어나 Exception의 report()가 실행된다.
- report는 가상함수도 아닐 뿐더러 부모 타입의 참조 변수로 참조했기 때문에 자신의 report()는 못 쓰고 Exception의 report()만 접근할 수 있게 된 것.
- 그러나 부모인 Exception 타입의 참조 변수로 받고 있기 때문에 객체 잘림 현상이 일어나 Exception의 report()가 실행된다.
-
객체 잘림 피하는 방법 1 : catch 순서
throw ArrayException();
...
catch (Exception & e)
{
e.report();
}
catch (ArrayException & e)
{
e.report();
}
💎출력💎
Exception report
- 위와 같이
Exception
타입으로 받는catch
를ArrayException
타입으로 받는catch
보다 위에 두면catch (Exception & e)
에서ArrayException();
를 받기 때문에 객체 잘림 현상이 일어나 Exception의 report()가 실행된다.- 이미 예외처리를 했으므로
catch (ArrayException & e)
는 실행되지 않는다.
throw ArrayException();
...
catch (ArrayException & e)
{
e.report();
}
catch (Exception & e)
{
e.report();
}
💎출력💎
Array Exception report
- 위와 같이
catch (ArrayException & e)
을 더 위에 배치시켜주면catch (ArrayException & e)
에서ArrayException();
를 받기 때문에 객체 잘림 현상 없이 ArrayException의 report()를 실행시킬 수 있다.- 이미 예외처리를 했으므로
catch (Exception & e)
는 실행되지 않는다.
- 이미 예외처리를 했으므로
객체 잘림 피하는 방법 2 : virtual
가상 함수
부모 타입의 참조 변수로 받아도 오버라이딩 한 내 본연의 함수를 호출하고 싶은 그런 함수들은 부모 예외 클래스에서 가상 함수로 지정해주자.
#include <iostream>
#include <exception>
class Exception
{
public:
virtual void report() { std::cerr << "Exception report" << std::endl; }
};
class Exception : public Exception
{
public:
virtual void report() override { std::cerr << "Array Exception report" << std::endl; }
};
catch 문 안에서 다시 throw 하기
#include <iostream>
class Exception
{
public:
void report()
{
cerr << "Exception report" << endl;
}
};
class ArrayException : public Exception
{
public:
void report() // 오버라이딩
{
cerr << "Array Exception report" << endl;
}
}
class MyArray
{
private:
int m_data[5];
public:
int & operator [] (const int & index)
{
if (index < 0 || index >= 5) throw ArrayException();
return m_data[index];
}
};
void doSomething()
{
MyArray my_array;
try
{
my_array[100];
}
catch (ArrayException & e)
{
cout << "doSomething()" << endl;
e.report();
throw e; // 👈 re-throw
}
catch (Exception & e)
{
cout << "doSomething()" << endl;
e.report();
}
}
int main()
{
try
{
doSomething();
}
catch (ArrayException & e)
{
cout << "main()" << endl;
e.report();
}
return 0;
}
💎출력💎
doSomething()
Array Exception report
main()
Array Exception report
- main 함수
try
에서doSomething()
호출- doSomething 함수
try
에서 my_array[]
연산자 오버로딩 호출- MyArray 클래스
throw ArrayException();
예외 발생 스택 되감기 👉 호출한 doSomething 지점으로 돌아감
- MyArray 클래스
catch (ArrayException & e)
에서ArrayException()
를 받아 예외 처리 한다.- ArrayException의 e.report() 실행
throw e
catch문 안에서 예외 또 발생 스택 되감기 👉 호출한 main 지점으로 돌아감- 그 바로 아래 있는
catch(Exception & e)
에서는 받지 않는다. 실행 자체가 안됨! 무조건 스택 되감기
- doSomething 함수
- main 함수의
catch (ArrayException & e)
에서e
를 받는다.- ArrayException의 e.report() 실행
예외가
throw
던져지면 알맞는 catch를 그 아래 부분에서 찾는 것이 아닌 스택 되감기해서 알맞는 catch를 찾는다는 것 잊지 말기!
#include <iostream>
class Exception
{
public:
void report()
{
cerr << "Exception report" << endl;
}
};
class ArrayException : public Exception
{
public:
void report() // 오버라이딩
{
cerr << "Array Exception report" << endl;
}
}
class MyArray
{
private:
int m_data[5];
public:
int & operator [] (const int & index)
{
if (index < 0 || index >= 5) throw ArrayException();
return m_data[index];
}
};
void doSomething()
{
MyArray my_array;
try
{
my_array[100];
}
catch (Exception & e)
{
cout << "doSomething()" << endl;
e.report();
throw e;
}
}
int main()
{
try
{
doSomething();
}
catch (ArrayException & e)
{
cout << "main()" << endl;
e.report();
}
catch (Exception & e)
{
cout << "main()" << endl;
e.report();
}
return 0;
}
💎출력💎
doSomething()
Exception report
main()
Exception report
- 위 예시에선
throw ArrayException();
를 해주고- 스택 되감기를 통해 doSomething 함수의
catch (Exception & e)
에서 이를 받는다. - 그리고
throw e
하고 또 예외를 발생시킨다.- 이때의 e는 ArrayException();을 받았더라도 객체 잘림 현상으로 인해 Exception 타입으로만 접근 가능하다.
- 따라서 main 함수에서
catch (ArrayException & e)
가 아닌catch (Exception & e)
에서 다시 던져진 이e
를 받게 된다.
- 따라서 main 함수에서
- 이때의 e는 ArrayException();을 받았더라도 객체 잘림 현상으로 인해 Exception 타입으로만 접근 가능하다.
- 스택 되감기를 통해 doSomething 함수의
객체 잘림 피하는 방법 3 : throw ;
catch (Exception & e)
{
cout << "doSomething()" << endl;
e.report();
throw;
}
💎출력💎
doSomething()
Exception report
main()
Array Exception report
throw e;
가 아닌 그냥throw;
만 해주면 원래 본연의 모습으로 던져지게 된다.
- doSomething 함수에서
catch (Exception & e)
로ArrayException()
을 받았기 때문에 Exception의 report가 실행된다.- “Exception report” 출력
throw;
다시 예외를 던질 땐 원래 본연의 모습인ArrayException
타입으로 던져진다.- 따라서 main 함수의
catch (ArrayException & e)
에서 이를 받게 된다. - “Array Exception report” 출력
- 따라서 main 함수의
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글남기기