예외 사양이 나쁜 이유는 무엇입니까?


50

약 10 년 전에 학교로 돌아와서 예외 지정자를 사용하도록 가르치고있었습니다. 내 배경은 그들 중 하나이기 때문에 강제하지 않으면 C ++을 완고하게 피하는 Torvaldish C 프로그래머이기 때문에 나는 C ++로만 끝나고 내가 할 때 예외 지정자를 계속 사용합니다.

그러나 대부분의 C ++ 프로그래머는 예외 지정자를 찌푸리는 것처럼 보입니다. 나는 이와 같은 다양한 C ++ 전문가들의 토론과 논쟁을 읽었다 . 내가 이해하는 한 세 가지로 요약합니다.

  1. 예외 지정자는 나머지 언어와 일치하지 않는 유형 시스템 ( "그림자 유형 시스템")을 사용합니다.
  2. 예외 지정자를 가진 함수가 지정한 것을 제외한 다른 것을 던지면 프로그램이 예기치 않은 나쁜 방식으로 종료됩니다.
  3. 향후 C ++ 표준에서는 예외 지정자가 제거됩니다.

여기에 뭔가 빠졌습니까? 아니면 모든 이유입니까?

내 의견 :

1)에 관해서 : 그래서 무엇. C ++은 아마도 문법적으로 가장 일관성이없는 프로그래밍 언어 일 것입니다. 우리는 매크로, goto / labels, undefined / unspecified / implementation-defined behavior의 horde (hoard?), 잘못 정의 된 정수 타입, 모든 암시 적 타입 프로모션 규칙, friend, auto와 같은 특수한 키워드를 가지고 있습니다. , 등록, 명시 적 ... 등등. 누군가 C / C ++에서 모든 이상한 점을 담은 두꺼운 책 몇 권을 쓸 수도 있습니다. 그렇다면 왜 사람들은이 특정한 불일치에 대해 반응합니까? 언어의 훨씬 더 위험한 다른 특징들과 비교할 때 사소한 결함입니까?

2) 관련 : 본인의 책임이 아닙니까? C ++에서 치명적인 버그를 작성할 수있는 다른 많은 방법이 있습니다.이 특별한 경우가 더 나쁜 이유는 무엇입니까? throw(int)Crash_t 를 작성 하고 던지는 대신 내 함수가 int에 대한 포인터를 반환한다고 주장 할 수 있습니다. C / C ++의 정신은 항상 대부분의 책임을 프로그래머에게 맡기는 것이 었습니다.

그렇다면 이점은 어떻습니까? 가장 분명한 것은 함수가 지정한 것과 다른 유형을 명시 적으로 던지려고하면 컴파일러에서 오류를 발생 시킨다는 것입니다. 나는 이것에 관해 표준이 명확하다고 생각합니다 (?). 버그는 함수가 다른 함수를 호출하여 잘못된 유형을 던질 때만 발생합니다.

결정 론적 임베디드 C 프로그램의 세계에서 왔을 때, 나는 함수가 무엇을 던질 지 정확히 알고 싶어합니다. 그것을 지원하는 언어로 된 것이 있다면 왜 사용하지 않습니까? 대안은 다음과 같습니다.

void func() throw(Egg_t);

void func(); // This function throws an Egg_t

호출자가 두 번째 경우에는 try-catch를 구현하는 것을 무시 / 잊어 버릴 가능성이 높지만 첫 번째 경우는 적습니다.

내가 알기로,이 두 가지 형식 중 하나가 갑자기 다른 종류의 예외를 던지기로 결정하면 프로그램이 중단됩니다. 첫 번째 경우에는 다른 예외를 던질 수 없기 때문에 두 번째 경우 아무도 SpanishInquisition_t를 던질 것으로 예상하지 않았으므로 표현식이 있어야 할 곳에서 잡히지 않기 때문입니다.

후자의 경우, 프로그램의 최상위 레벨에서 최후의 어획량 캐치 (...)를 갖는 것이 실제로 프로그램 충돌보다 더 나은 것처럼 보이지는 않습니다. "프로그램의 어딘가에 무언가 처리되지 않은 예외가 발생했습니다 ". 예외가 발생한 곳에서 멀어지면 프로그램을 복구 할 수 없습니다. 프로그램을 종료하는 것이 유일한 방법입니다.

그리고 사용자의 관점에서 그들은 "프로그램이 종료되었습니다. 주소 0x12345의 Blablabla"라는 메시지 나 OS에서 "처리되지 않은 예외 : myclass. func.something "을 참조하십시오. 버그는 여전히 존재합니다.


다가오는 C ++ 표준을 사용하면 예외 지정자를 포기하는 것 외에 다른 옵션이 없습니다. 그러나 나는 "그의 거룩함이 말 했으므로 그렇게 나쁜 것"보다는 그들이 왜 나쁜지에 대한 확실한 주장을 듣고 싶습니다. 아마도 내가 열거 한 것보다 더 많은 주장이 있거나, 내가 아는 것보다 더 많은 주장이 있습니까?


30
나는 이것을 "질문, 위장한 질문"으로 내려 놓고 싶어한다. 당신은 E.specs에 대해 세 가지 유효한 포인트를 요구하지만 왜 당신은 C ++와 같은 성가신 그리고 나 같은 C- 더 나은 엉망으로 우리를 귀찮게합니까?
Martin Ba

10
@ 마틴 : 나는 모든 세부 사항에 편견이 있고 최신 상태가 아니라는 것을 지적하고 싶지만 언어를 순진하고 /하거나 아직 망쳐 놓지 않은 눈으로 언어를 생각합니다. 그러나 C C ++는 이미 굉장히 결함이있는 언어이므로 어느 정도의 결함은 실제로 중요하지 않습니다. 게시물은 편집하기 전에 실제로 훨씬 나빴습니다. :) 예외 지정자에 대한 주장도 매우 주관적이므로 주관적이지 않고 논의하기가 어렵습니다.

SpanishInquisition_t! 재밌어요! 나는 개인적으로 예외를 던지기 위해 스택 프레임 포인터를 사용하여 깊은 인상을 받았으며 코드를 훨씬 더 깨끗하게 만들 수있는 것처럼 보입니다. 그러나 실제로 예외를 제외하고는 코드를 작성한 적이 없습니다. 구식이라고 부르십시오. 그러나 반환 값은 저에게 잘 작동합니다.
Shahbaz

@Shahbaz 한 줄 사이에서 읽을 수 있듯이, 나는 구식이지만 여전히 스스로 예외가 좋은지 나쁜지는 결코 묻지 않았습니다.

2
의 과거 시제 투사가 되어 던졌다 하지 throwed . 강력한 동사입니다.
TRiG December

답변:


48

예외 사양은 약하게 시행되어 실제로 많은 것을 달성하지 못하기 때문에 좋지 않습니다. 런타임에서 UB를 호출하는 대신 종료 () 할 수 있도록 예기치 않은 예외를 검사하도록 강제하기 때문에 나쁩니다. 이로 인해 상당한 양의 성능이 낭비 될 수 있습니다.

요약하면 예외 코드는 실제로 코드를 더 안전하게 만들 정도로 언어에 강하게 적용되지 않았으며 지정된대로 구현하면 성능이 크게 저하되었습니다.


2
그러나 런타임 검사는 예외 사양의 결함이 아니라 잘못된 유형이 던져지면 종료가 있어야한다는 표준의 일부가 아닙니다. 이 동적 검사로 이어지는 요구 사항을 간단히 제거하고 컴파일러가 모든 것을 정적으로 평가하게하는 것이 더 똑똑하지 않습니까? RTTI가 진정한 범인이라고 들리거나 ...?

7
@Lundin : C ++의 함수에서 발생할 수있는 모든 예외를 정적으로 결정할 수는 없습니다. 언어는 런타임에 제어 흐름에 대한 임의적 및 비 결정적 변경 (예 : 함수 포인터, 가상 메소드 및 등). Java와 같은 정적 컴파일 타임 예외 검사를 구현하려면 언어를 근본적으로 변경하고 많은 C 호환성을 비활성화해야합니다.

3
@ OrbWeaver : 정적 검사가 가능하다고 생각합니다. 예외 사양은 반환 값과 같은 함수 사양의 일부이며 함수 포인터, 가상 재정의 등은 호환 가능한 사양을 가져야합니다. 주된 반대 의견은 그것이 전적으로 또는 전혀없는 기능이라는 것입니다. 정적 예외 사양을 가진 함수는 레거시 함수를 호출 할 수 없습니다. (C 함수 호출은 아무 것도 던질 수 없으므로 좋을 것입니다).
Mike Seymour

2
@Lundin : 예외 사양은 제거되지 않았으며 더 이상 사용되지 않습니다. 그것들을 사용하는 레거시 코드는 여전히 유효합니다.
마이크 시모어

1
@Lundin : 예외 사양을 가진 구 버전의 C ++는 완벽하게 호환됩니다. 이전에 코드가 유효했던 경우 컴파일에 실패하거나 예기치 않게 실행되지 않습니다.
DeadMG

18

아무도 사용하지 않는 한 가지 이유는 기본 가정이 잘못 되었기 때문입니다.

"가장 명백한 [이점]은 함수가 사용자가 지정한 것과 다른 유형을 명시 적으로 던지려고하면 컴파일러가 오류를 발생 시킨다는 것입니다."

struct foo {};
struct bar {};

struct test
{
    void baz() throw(foo)
    {
        throw bar();
    }
};

int main()
{
    test x;
    try { x.baz(); } catch(bar &b) {}
}

이 프로그램은 오류나 경고없이 컴파일됩니다 .

또한 예외가 발생하더라도 프로그램은 여전히 ​​종료됩니다.


편집 : 질문의 특정 부분에 대답하려면 catch (...) (또는 더 나은 catch (std :: exeption &) 또는 기타 기본 클래스를 누른 다음 catch (...))는 여전히 유용하지 않습니다. 무엇이 잘못되었는지 정확히 알고 있습니다.

사용자가 "저장"메뉴 버튼을 쳤다고 가정하십시오. 메뉴 버튼의 핸들러는 애플리케이션을 저장하도록 초대합니다. 파일이 사라진 네트워크 리소스에 있고, 읽기 전용 파일이 있고, 파일을 저장할 수없는 등의 이유로 여러 가지 이유로 실패 할 수 있습니다. 그러나 처리기는 실제로 실패한 이유를 신경 쓰지 않습니다. 그것은 단지 성공 또는 실패를 신경 쓰는 것입니다. 성공하면 모든 것이 좋습니다. 실패하면 사용자에게 알릴 수 있습니다. 예외의 정확한 특성은 관련이 없습니다. 또한, 예외가없는 적절한 코드를 작성하면 오류를 신경 쓰지 않는 코드의 경우에도 이러한 오류가 발생하지 않고 시스템을 통해 전파 될 수 있습니다. 이를 통해 시스템을보다 쉽게 ​​확장 할 수 있습니다. 예를 들어, 이제 데이터베이스 연결을 통해 저장합니다.


4
@Lundin 경고 없이 컴파일되었습니다 . 그리고 당신은 할 수 없습니다. 확실하지 않습니다. 함수 포인터를 통해 호출하거나 가상 멤버 함수 일 수 있으며 파생 클래스는 파생 클래스에서 파생 클래스를 알 수 없습니다.
Kaz Dragon

힘든 행운. Embarcadero / Borland는 "예외가 예외 지정자를 위반합니다"라는 경고를 표시합니다. 분명히 GCC는 그렇지 않습니다. 그래도 경고를 구현하지 못한 이유는 없습니다. 마찬가지로 정적 분석 도구가이 버그를 쉽게 찾을 수 있습니다.

14
@ 룬딘 : 허위 정보를 주장하고 반복하지 마십시오. 귀하의 질문은 이미 격렬한 것이 었으며 허위 주장이 도움이되지 않습니다. 정적 분석 도구는 이러한 상황이 발생했는지 확인할 수 없습니다. 그렇게하려면 중단 문제를 해결해야합니다. 또한 전체 프로그램을 분석해야하며 종종 전체 소스를 사용할 수 없습니다.
David Thornley

1
@Lundin 같은 정적 분석 도구는 어떤 예외가 스택을 떠 났는지 알려줄 수 있으므로이 경우 예외 사양을 사용하면 오류 사례를 처리 할 수있는 프로그램을 중단시킬 수있는 잠재적 인 부정을 제외하고는 아무것도 사지 않습니다. 훨씬 더 좋은 방법 (예 : 실패 알림 및 계속 실행과 같은 예 : 내 대답의 후반 참조).
Kaz Dragon

1
@ 룬딘 좋은 질문입니다. 대답은 일반적으로 호출하는 함수에서 예외가 발생하는지 여부를 알지 못하므로 안전한 가정은 모두 예외입니다. 이것을 잡을 곳은 stackoverflow.com/questions/4025667/… 에서 대답 한 질문입니다 . 이벤트 핸들러는 특히 자연스러운 모듈 경계를 형성합니다. 예외가 일반 창 / 이벤트 시스템 (예 :)으로 빠져 나가도록 허용하면 처벌됩니다. 프로그램이 그곳에서 살아남 으려면 핸들러가 모두 잡아야합니다.
Kaz Dragon

17

Anders Hejlsberg와의 인터뷰 는 매우 유명합니다. 여기에서 C # 디자인 팀이 먼저 확인 된 예외를 버린 이유를 설명합니다. 간단히 말해 버전 화와 확장 성의 두 가지 주요 이유가 있습니다.

OP는 C ++에 중점을두고 있지만 Hejlsberg는 C #에 대해 논의하고 있지만 Hejlsberg가 만드는 점은 C ++에도 완벽하게 적용됩니다.


5
"Hejlsberg가 만드는 점은 C ++에도 완벽하게 적용됩니다." Java로 구현 된 확인 된 예외 는 호출자가 예외 (및 / 또는 호출자의 호출자 등)를 처리하도록합니다. C ++의 예외 사양 (약한 약하고 쓸모없는)은 단일 "함수 호출 경계"에만 적용되며 확인 된 예외와 같이 "호출 스택을 전파" 하지 않습니다 .
Martin Ba

3
@ Martin : C ++의 예외 사양 동작에 대해 동의합니다. 그러나 "Hejlsberg가 만드는 점은 C ++에도 완벽하게 적용 할 수 있습니다"라고 인용 한 문구는 C ++ 사양이 아니라 Hejlsberg가 만드는 점을 말합니다. 다시 말해, 예외 전파가 아닌 프로그램의 버전 및 확장성에 대해 이야기하고 있습니다.
CesarGon

3
나는 Hejlsberg에 대해 매우 일찍 동의하지 않았다. 확인 된 예외는 아무런 도움이되지 않습니다. 이러한 독재 적 API 디자이너는 예외 처리 방법을 알려줍니다. " --- 예외 확인 여부 는 여전히 호출하는 API에서 발생한 예외를 처리해야합니다. 확인 된 예외는 단순히 명시 적으로 만듭니다.
quant_dev

5
@quant_dev : 아니요, 호출하는 API에서 발생한 예외를 처리 할 필요가 없습니다. 완벽하게 유효한 옵션은 예외를 처리 하지 않고 발신자가 처리 할 수 ​​있도록 호출 스택을 버블 링하는 것입니다.
CesarGon

4
@CesarGon 예외를 처리하지 않으면 예외 처리 방법도 선택됩니다.
quant_dev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.