[c++] C ++에서 예외 지정자를 사용해야합니까?

C ++에서는 예외 지정자를 사용하여 함수가 예외를 throw하거나 throw하지 않도록 지정할 수 있습니다. 예를 들면 :

void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type

다음과 같은 이유로 실제로 사용하는 것이 의문입니다.

  1. 컴파일러는 엄격한 방식으로 예외 지정자를 실제로 적용하지 않으므로 이점이 크지 않습니다. 이상적으로는 컴파일 오류가 발생합니다.
  2. 함수가 예외 지정자를 위반하면 표준 동작은 프로그램을 종료하는 것이라고 생각합니다.
  3. VS.Net에서는 throw (X)를 throw (…)로 취급하므로 표준 준수가 강하지 않습니다.

예외 지정자를 사용해야한다고 생각하십니까?
“예”또는 “아니오”로 대답하고 귀하의 대답을 정당화하는 몇 가지 이유를 제공하십시오.



답변

아니.

그 이유는 다음과 같습니다.

  1. 템플릿 코드는 예외 사양으로 작성이 불가능합니다.

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }

    복사본이 발생하고 매개 변수 전달이 x()발생하며 알 수없는 예외가 발생할 수 있습니다.

  2. 예외 사양은 확장 성을 방해하는 경향이 있습니다.

    virtual void open() throw( FileNotFound );

    로 진화 할 수 있습니다

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );

    당신은 정말로 그것을 다음과 같이 쓸 수 있습니다.

    throw( ... )

    첫 번째는 확장 할 수없고 두 번째는 지나치게 야심적이며 세 번째는 가상 함수를 작성할 때 실제로 의미하는 바입니다.

  3. 레거시 코드

    다른 라이브러리에 의존하는 코드를 작성할 때 무언가 끔찍하게 잘못되었을 때 어떤 일이 발생할지 알 수 없습니다.

    int lib_f();
    
    void g() throw( k_too_small_exception )
    {
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }

    glib_f()던질 때 종료됩니다 . 이것은 (대부분의 경우) 당신이 정말로 원하는 것이 아닙니다. std::terminate()호출해서는 안됩니다. 조용히 / 폭력적으로 죽는 것보다 스택 추적을 검색 할 수있는 처리되지 않은 예외로 애플리케이션이 충돌하도록하는 것이 항상 좋습니다.

  4. 일반적인 오류를 반환하고 예외적 인 경우에 발생하는 코드를 작성합니다.

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    {
       MessageUser( "out of memory, exiting process" );
       throw;
    }

그럼에도 불구하고 라이브러리에서 자체 예외를 던질 때 예외 사양을 사용하여 의도를 나타낼 수 있습니다.


답변

C ++에서 예외 사양을 피하십시오. 귀하의 질문에 제공하는 이유는 그 이유에 대한 꽤 좋은 시작입니다.

Herb Sutter의 “A Pragmatic Look at Exception Specifications”를 참조하십시오 .


답변

표준 예외 (C ++의 경우)
예외 지정자는 대부분 실패한 C ++ 표준의 실험 이라고 생각합니다 .
예외는 no throw 지정자가 유용하지만 코드가 지정자와 일치하는지 확인하기 위해 내부적으로 적절한 try catch 블록을 추가해야한다는 것입니다. Herb Sutter에는 주제에 대한 페이지가 있습니다. 고치 82

또한 예외 보장을 설명 할 가치가 있다고 생각합니다.

이것들은 기본적으로 객체의 상태가 해당 객체의 메소드를 이스케이프하는 예외에 의해 어떻게 영향을 받는지에 대한 문서입니다. 불행히도 그들은 컴파일러에 의해 강제되거나 언급되지 않습니다.
부스트 및 예외

예외 보장

보장 없음 :

예외가 메서드를 이스케이프 한 후 개체의 상태에 대한 보장이 없습니다
. 이러한 상황에서는 개체를 더 이상 사용하지 않아야합니다.

기본 보증 :

거의 모든 상황에서 이것이 방법이 제공하는 최소한의 보증이어야합니다.
이렇게하면 개체의 상태가 잘 정의되고 지속적으로 사용할 수 있습니다.

강력한 보증 : (일명 거래 보증)

이렇게하면 메서드가 성공적으로 완료
되거나 예외가 throw되고 개체 상태가 변경되지 않습니다.

던짐 없음 보장 :

이 메서드는 예외가 메서드 밖으로 전파되지 않도록합니다.
모든 소멸자는이 보장을해야합니다.
| NB 예외가 이미 전파되는 동안 예외가 소멸자를 이스케이프하는 경우
| 응용 프로그램이 종료됩니다


답변

gcc는 예외 사양을 위반하면 경고를 표시합니다. 내가하는 일은 예외가 내 문서와 일치하는지 확인하기 위해 명시 적으로 “lint”모드 컴파일에서만 예외 사양을 사용하는 매크로를 사용하는 것입니다.


답변

유일하게 유용한 예외 지정자는 “throw ()”입니다.


답변

예외 사양은 C ++에서 놀랍도록 유용한 도구가 아닙니다. 그러나 std :: unexpected와 결합하면 좋은 용도가 있습니다.

일부 프로젝트에서 내가하는 일은 예외 사양이있는 코드이고, 내 디자인의 특별한 예외를 발생시키는 함수로 set_unexpected ()를 호출하는 것입니다. 이 예외는 생성시 역 추적 (플랫폼 별 방식)을 가져오고 std :: bad_exception에서 파생됩니다 (원하는 경우 전파 할 수 있도록 허용). 보통과 같이 종료 () 호출이 발생하면 역 추적은 what ()에 의해 인쇄됩니다 (및이를 유발 한 원래 예외, 찾기 어렵지 않음). 따라서 계약이 어디에 있는지 정보를 얻습니다. 어떤 예기치 않은 라이브러리 예외가 발생했는지 등을 위반했습니다.

이렇게하면 라이브러리 예외 (표준 예외 제외)의 전파를 허용하지 않으며 std :: exception에서 모든 예외를 파생합니다. 라이브러리가 던지기로 결정하면 항상 코드를 제어 할 수 있도록 잡아서 내 계층 구조로 변환합니다. 종속 함수를 호출하는 템플릿 함수는 명백한 이유로 예외 사양을 피해야합니다. 그러나 어쨌든 라이브러리 코드가있는 템플릿 함수 인터페이스를 갖는 경우는 드뭅니다 (실제로 유용한 방식으로 템플릿을 사용하는 라이브러리는 거의 없습니다).


답변

함수 선언을 둘러싼 주석보다 함수 선언을보고 싶어하는 사람들이 사용할 코드를 작성하는 경우 사양이 포착하려는 예외를 알려줍니다.

그렇지 않으면 어떤 것도 사용하는 것이 특히 유용 throw()하지 않지만 예외를 throw하지 않는다는 것을 나타냅니다.