C++ Chapter 14.3 : 예외 클래스와 상속

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 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 함수의 trymy_array[100]
    • 👉 []연산자 오버로딩 호출
      • index가 5 이상이므로 예외 발생 throw -1
      • return m_data[index]는 실행되지 않는다.
    • 👉 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()만 접근할 수 있게 된 것.


객체 잘림 피하는 방법 1 : catch 순서


    throw ArrayException();

    ...

    catch (Exception & e)
    {
        e.report();
    }
    catch (ArrayException & e)
    {
        e.report();
    }
💎출력💎

Exception report
  • 위와 같이 Exception 타입으로 받는 catchArrayException 타입으로 받는 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 지점으로 돌아감
        • catch (ArrayException & e) 에서 ArrayException()를 받아 예외 처리 한다.
          • ArrayException의 e.report() 실행
          • throw e catch문 안에서 예외 또 발생 스택 되감기 👉 호출한 main 지점으로 돌아감
          • 그 바로 아래 있는 catch(Exception & e)에서는 받지 않는다. 실행 자체가 안됨! 무조건 스택 되감기
    • 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를 받게 된다.

객체 잘림 피하는 방법 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” 출력


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Cpp 카테고리 내 다른 글 보러가기

댓글 남기기