스토리텔링 개발자

[More Effective C++] 13. catch 매개변수는 참조자 본문

개발/More Effective C++

[More Effective C++] 13. catch 매개변수는 참조자

김디트 2024. 8. 16. 14:40
728x90

항목 13. 발생한 예외는 참조자로 받아내자

 

 

 

catch 매개변수를 포인터로(catch-by-pointer)
  • 이론 상으로는 포인터로 받는 것이 가장 효율적이어야 한다.(항목 15 참조)
    • 객체의 복사 없이도 전달이 이루어지기 때문이다.
class exception { ... };

void someFunction()
{
    static exception ex; // 범위를 벗어나도 살리기 위해 static 선언
    // exception ex; // 이처럼 선언하기라도 하면 미정의 동작을 유발한다!!
    ...
    throw &ex; // 포인터 객체를 던진다.
    ...
}

void doSomething()
{
    try
    {
        someFunction();
    }
    catch(exception* ex) // exception*를 받는다.
    {
        ...
    }
}
  • 힙 객체를 새로 만들고, 그 객체를 예외로 던지는 방식은 어떨까?
void someFunction()
{
    ...
    throw new exception; // 괜찮은 것 같지만..
    // 1. operator new가 예외를 발생시키면 어쩌나?
    // 2. 힙에 할당한 메모리는 누가 해제해주나?
    ...
}

 

  • 예외를 받는 측에서는 모순이 발생한다.
    • 힙에 할당한 메모리의 포인터인지 아닌지 알 방도가 없으므로 delete 할지 말지 결정할 수 없다.
    • static이나 전역 변수의 포인터에 대해 delete를 하면 큰일이다.
  • 또 하나의 문제
    • C++ 표준 예외는 포인터가 아니라 객체로 발생한다.
      1. bad_alloc
        • operator new가 메모리 요구를 만족시키지 못했을 때 발생
      2. bad_cast
      3. bad_typeid
        • dynamic_cast를 널 포인터에 적용했을 때 발생
      4. bad_exception
    • 즉, 참조자나 값으로 받는 게 아니면 표준 예외들은 받을 수 없다.

 

 

 

catch 매개변수를 값으로(catch-by-value)
  •  
  • 효율
    • 늘 두 번씩 복사되기 때문에 비효율적이다.(항목 12 참조)
  • 슬라이스 문제(slicing problem)
    • 파생 클래스 객체가 기본 클래스를 받는 catch문에 들어가면?
      • 파생 클래스 부분이 잘려나간다.(sliced off)
    • 이 문제는 찾아내기 무척 까다롭다.
class exception
{
public:
    virtual const char* what() throw();
    ...
};

class runtime_error : public exception { ... };

class Validation_error : public runtime_error
{
public:
    virtual const char* what() throw(); // exception의 것을 재정의
    ...
};

void someFunction()
{
    ...
    if(유효성 검사가 실패한다면)
    {
        throw Validation_error();
    }
    ...
}

void doSomething()
{
    try
    {
        someFunction();
    }
    catch(exception ex)
    {
        cerr << ex.what(); // 슬라이스 문제!!
        // exception::what()이 호출된다.
        // Validation_error::what()은 절대 호출되지 않는다.
        ...
    }
}

 

 

 

catch 매개변수를 참조자로(catch-by-reference)
  • 모든 것이 행복하다.
  • 포인터로 받을때와 비교하면,
    • 객체 삭제에 대한 고민이 필요 없다.
    • C++ 표준 예외를 처리할 수 있다.
  • 값으로 받을때와 비교하면,
    • 예외 객체는 한 번만 복사된다.
    • 슬라이스 문제가 없다.
void someFunction()
{
    ...
    if(유효성 검사 실패 시)
    {
        throw Validation_error();
    }
    ...
}

void doSomething()
{
    try
    {
        someFunction();
    }
    catch(exception& ex) // 단지 &를 붙였을 뿐인데..
    {
        cerr << ex.what(); // Validation_error::what()를 호출한다!!
        ...
    }
}

 

 

 

참조
 

[Effective C++] 20. const & 전달하기

항목 20. ‘값에 의한 전달’보다는 ‘상수 객체 참조자에 의한 전달’ 방식을 택하는 편이 대개 낫다   값에 의한 전달C++는 함수로부터 객체를 전달받거나 함수에서 객체 전달시 ‘값에 의한

delightlane.tistory.com

 

728x90
Comments