문제

C++에서는 예외 지정자를 사용하여 함수가 예외를 발생시킬 수도 있고 발생하지 않을 수도 있음을 지정할 수 있습니다.예를 들어:

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();
    }
    

    g 종료됩니다. lib_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의 글 보기 "예외 사양에 대한 실용적인 시각".

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

추가적으로 예외 보장에 대해 설명할 가치가 있다고 생각합니다.

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

예외 보장

보증 없음:

예외가 메서드를 이스케이프한 후 객체의 상태에 대한 보장은 없습니다.
이러한 상황에서는 해당 개체를 더 이상 사용해서는 안 됩니다.

기본 보증:

거의 모든 상황에서 이는 메소드가 제공하는 최소한의 보장이어야 합니다.
이는 객체의 상태가 잘 정의되어 있고 계속 일관되게 사용될 수 있음을 보장합니다.

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

이는 메소드가 성공적으로 완료됨을 보장합니다.
아니면 예외가 발생하고 객체 상태가 변경되지 않습니다.

던짐 금지 보장:

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

예외 사양을 위반하면 gcc에서 경고를 내보냅니다.내가 하는 일은 매크로를 사용하여 "lint" 모드 컴파일에서만 예외 사양을 사용하여 예외가 내 문서와 일치하는지 확인하는 것입니다.

유일한 유용한 예외 지정자는 "throw()"입니다. "throw()"는 "던지지 않습니다"와 같습니다.

아니요.이를 사용하고 코드 또는 코드에 의해 호출된 코드에 의해 지정하지 않은 예외가 발생하는 경우 기본 동작은 프로그램을 즉시 종료하는 것입니다.

또한 C++0x 표준의 현재 초안에서는 해당 사용이 더 이상 사용되지 않는다고 생각합니다.

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

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

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

주변의 주석보다 함수 선언을 살펴보는 것을 선호하는 사람들이 사용할 코드를 작성하는 경우 사양은 그들이 포착하고 싶은 예외가 무엇인지 알려줄 것입니다.

그렇지 않으면 다른 것을 사용하는 것이 특히 유용하다고 생각하지 않습니다. throw() 예외가 발생하지 않음을 나타냅니다.

"throw()" 사양을 사용하면 함수가 절대 예외를 발생시키지 않는다는 것을 알고 있는 경우(또는 적어도 예외를 발생시키지 않겠다고 약속하는 경우) 코드 흐름 분석을 수행할 때 컴파일러가 일부 최적화를 수행할 수 있습니다.Larry Osterman은 이에 대해 간략하게 다음과 같이 설명합니다.

http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx

일반적으로 나는 예외 지정자를 사용하지 않습니다.그러나 문제의 함수에서 다른 예외가 발생하면 프로그램이 확실히 이를 수행할 수 없는 경우 옳은, 그러면 유용할 수 있습니다.모든 경우에 해당 함수에서 어떤 예외가 예상되는지 명확하게 문서화해야 합니다.

예, 예외 지정자가 있는 함수에서 발생하는 지정되지 않은 예외의 예상 동작은 종료()를 호출하는 것입니다.

나는 또한 Scott Meyers가 More Effective C++에서 이 주제를 다루고 있다는 점에 주목하겠습니다.그의 Effective C++ 및 More Effective C++는 적극 권장되는 책입니다.

예, 내부 문서에 관심이 있다면 가능합니다.아니면 다른 사람들이 사용할 라이브러리를 작성하여 문서를 참조하지 않고도 무슨 일이 일어나는지 알 수 있을 수도 있습니다.던지거나 던지지 않는 것은 거의 반환 값과 마찬가지로 API의 일부로 간주될 수 있습니다.

동의합니다. 컴파일러에서 Java 스타일의 정확성을 적용하는 데는 실제로 유용하지 않지만, 아무 것도 없거나 무계획적인 주석보다는 낫습니다.

테스트를 작성할 때 함수가 실패할 때 무엇을 던질지 알 수 있도록 단위 테스트에 유용할 수 있지만 컴파일러에서는 이를 둘러싼 시행이 없습니다.C++에서는 필요하지 않은 추가 코드라고 생각합니다.어느 쪽을 선택하든 확신해야 할 것은 코드를 계속 읽을 수 있도록 프로젝트와 팀 구성원 전체에서 동일한 코딩 표준을 따른다는 것입니다.

기사에서 :

http://www.boost.org/community/Exception_safety.html

"예외 안전 제네릭 컨테이너를 작성하는 것은 불가능한 것으로 잘 알려져 있습니다." 이 주장은 종종 Tom Cargill [4]의 기사를 참조하여 일반적인 스택 템플릿에 대한 예외 안전 문제를 탐구합니다.그의 기사에서 Cargill은 많은 유용한 질문을 제기하지만 불행히도 그의 문제에 대한 해결책을 제시하지 못합니다 .1 그는 해결책이 불가능할 수 있다고 제안함으로써 결론을 내립니다.불행하게도, 그의 기사는 많은 사람들이 그 추측의“증거”로 읽었습니다.출판 된 이래로 예외 안전 제네릭 구성 요소의 많은 예가 있었으며, 그중에는 C ++ 표준 라이브러리 컨테이너가 있습니다.

그리고 실제로 템플릿 클래스 예외를 안전하게 만드는 방법을 생각할 수 있습니다.모든 하위 클래스를 제어할 수 없다면 어쨌든 문제가 발생할 수 있습니다.이를 수행하려면 다양한 템플릿 클래스에서 발생하는 예외를 정의하는 typedef를 클래스에 생성할 수 있습니다.문제는 처음부터 디자인하는 것이 아니라 항상 나중에 추가하는 것이라고 생각하며, 실제 장애물은 바로 이 오버헤드라고 생각합니다.

예외 사양 = 쓰레기입니다. 30세 이상의 Java 개발자에게 문의하세요.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top