예외 처리는 언제 어떻게 사용해야합니까?


82

예외 처리에 대해 읽고 있습니다. 예외 처리가 무엇인지에 대한 정보를 얻었지만 몇 가지 질문이 있습니다.

  1. 언제 예외가 발생합니까?
  2. 예외를 던지는 대신 반환 값을 사용하여 오류를 나타낼 수 있습니까?
  3. try-catch 블록으로 모든 기능을 보호하면 성능이 저하되지 않습니까?
  4. 언제 예외 처리를 사용합니까?
  5. 해당 프로젝트의 모든 함수가 try-catch 블록을 포함하는 프로젝트를 보았습니다 (즉, 전체 함수 내부의 코드가 try-catch 블록으로 둘러싸여 있음). 이것이 좋은 습관입니까?
  6. try-catch와 __try __except의 차이점은 무엇입니까?


"예외 발생시기"보다 더 구체적인 질문을해야합니다. 예외가 발생하면 예외를 throw합니다.
Falmarri 2010

나는 이것에 대해 PHP와 관련하여 썼지 만 거의 모든 것이 C ++에 적용될 수 있다고 생각합니다. 블로그 게시물을 확인하십시오 .
ircmaxell 2013

답변:


95

필자가 반드시 읽어야한다고 생각하는 예외에 대한 포괄적 인 가이드는 다음과 같습니다.

예외 및 오류 처리 -C ++ FAQ 또는 C ++ FAQ Lite

일반적으로 프로그램이 외부를 식별 할 수있을 때 예외를 발생시킵니다.실행을 방해하는 문제. 서버에서 데이터를 수신하고 해당 데이터가 유효하지 않은 경우 예외를 발생시킵니다. 디스크 공간이 부족합니까? 예외를 던집니다. 우주 광선이 데이터베이스 쿼리를 방해합니까? 예외를 던집니다. 그러나 자신의 프로그램 내부에서 잘못된 데이터를 얻는 경우 예외를 throw하지 마십시오. 문제가 자신의 잘못된 코드에서 비롯된 경우 ASSERT를 사용하여이를 방지하는 것이 좋습니다. 사용자가 처리 할 수 ​​있기 때문에 프로그램이 처리 할 수없는 문제를 식별하고 사용자에 대해 알리려면 예외 처리가 필요합니다. 그러나 프로그램의 버그는 사용자가 처리 할 수있는 것이 아니므로 프로그램 충돌은 "answer_to_life_and_universe_and_everything의 값이 42가 아닙니다! 절대로 발생하지 않아야합니다 !!!! 11"예외보다 적지 않습니다.

메시지 상자 표시와 같이 유용한 작업을 수행 할 수있는 예외를 포착하십시오. 어떻게 든 사용자 입력을 처리하는 함수 내에서 예외를 포착하는 것을 선호합니다. 예를 들어, 사용자가 "Annihilate all hunams"버튼을 누르고 annihilateAllHunamsClicked () 함수 안에 "I ca n't"라고 말하는 try ... catch 블록이 있습니다. hunamkind의 소멸은 수십, 수십 개의 함수를 호출해야하는 복잡한 작업이지만 한 번의 try ... catch 만 있으면됩니다. 왜냐하면 사용자에게는 하나의 버튼 클릭이라는 원자 적 작업이기 때문입니다. 모든 기능의 예외 검사는 중복되고 추악합니다.

또한 RAII에 익숙해지는 것이 좋습니다. 즉, 초기화 된 모든 데이터가 자동으로 삭제되도록하는 것입니다. 스택에서 가능한 한 많이 초기화하고 힙에서 무언가를 초기화해야 할 때 일종의 스마트 포인터를 사용하면됩니다. 스택에서 초기화 된 모든 것은 예외가 발생하면 자동으로 소멸됩니다. C 스타일 덤 포인터를 사용하는 경우 예외 발생시이를 정리할 사람이 없기 때문에 예외가 throw 될 때 메모리 누수가 발생할 위험이 있습니다 (확실히 C 스타일 포인터를 클래스의 멤버로 사용할 수 있지만 소멸자에서 처리).


귀중한 의견을 보내 주셔서 감사합니다. > "사용자가 처리 할 수 ​​있기 때문에 프로그램이 처리 할 수없는 문제를 식별하고 사용자에 대해 알리려면 예외 처리가 필요합니다."예외를 던지는 대신 오류 값을 반환 할 수 있으며 호출 함수에서 오류를 확인할 수 있습니다. 사용자가 처리하는 데 도움이되는 메시지를 표시합니다.
Umesha MS

특히이 섹션의 세 번째 및 네 번째 코드 스 니펫에서 FAQ를 다시 살펴보십시오. parashift.com/c++-faq-lite/exceptions.html#faq-17.2 예외는 모든 깊이에서 오류가 드러날 수 있기 때문에 훌륭합니다. 호출 스택의 ... 오류 코드가 해저면, 예외는 잠수함과 같습니다. :)
Septagram

2
@Septagram "문제가 자신의 잘못된 코드에서 비롯된 경우 ASSERT를 사용하여이를 방지하는 것이 좋습니다.": 어설 션은 버그가있는 브랜치에 의존하지 않는 작업을 계속하도록 허용하지 않습니다 (단지 프로그램이 한 기능에 버그가 있다고해서 다른 유용한 일을 할 수 없다는 의미는 아닙니다.) 사용자 정의 로거를 사용할 수 없습니다. RAII를 사용하여 혜택을받는 소멸자를 우회합니다 (프로그램이 종료되기 전에 파일 버퍼를 플러시 하시겠습니까? 그 때 소멸자를 건너 뛰지 않는 것이 fclose좋습니다!). 전체 스택 추적을 제공하지 않습니다.
daemonspring

RAII와 "멍청한 포인터"에 대해 말할 것. "벙어리 포인터"를 사용한다고해서 RAII를 따르지 않는다는 의미는 아닙니다. 시스템 리소스를 할당 할 때만 할당이 해제되었는지 확인해야합니다. 포인터는 단순히 메모리 주소를 보유하는 스택의 변수입니다. "덤 포인터"를 사용하기 위해 객체 할당 및 초기화가 필요하지 않습니다. "멍청한 포인터"는 다른 곳에 할당 된 객체의 주소를 단순히 보유 할 수 있으며 사용하기에 완벽합니다. (계속 다음 주석 인치)
SeanRamey

Display에 사물을 그리기 위해 포인터에 Display 객체의 주소를 저장하는 Renderer 객체가있을 수 있지만, Renderer 객체를 파괴 할 때 스마트 포인터가 Display 객체를 파괴하기 때문에 스마트 포인터를 사용할 수 없습니다. , 당신이 원하지 않는. 이 경우 "벙어리 포인터"또는 참조가 유일한 방법입니다.
SeanRamey 2017

12

예외는 다양한 상황에서 유용합니다.

첫째, 전제 조건을 계산하는 비용이 너무 높기 때문에 계산을 수행하고 전제 조건이 충족되지 않은 경우 예외를두고 중단하는 것이 더 좋은 기능이 있습니다. 예를 들어, 특이 행렬을 반전 할 수는 없지만, 특이 행렬을 계산하려면 매우 비용이 많이 드는 행렬식을 계산해야합니다. 어쨌든 함수 내에서 수행해야 할 수도 있으므로 행렬을 반전하고보고 할 때 "시도해보십시오". 예외를 던져서 할 수없는 경우 오류. 이것은 기본적 으로 부정적인 전제 조건 사용 으로 예외 입니다.

그런 다음 코드가 이미 복잡하고 오류 정보를 콜 체인으로 전달하기 어려운 다른 경우가 있습니다. 이것은 부분적으로 C와 C ++가 손상된 데이터 구조 모델을 가지고 있기 때문입니다. 다른 더 나은 방법이 있지만 C ++는이를 지원하지 않습니다 (예 : Haskell에서 모나드 사용). 이 사용은 기본적으로 나는 그것을 옳게하는 것을 귀찮게 할 수 없기 때문에 예외를 던질 것입니다 . 올바른 방법은 아니지만 실용적입니다.

그런 다음 예외의 주요 용도가 있습니다. 메모리 나 디스크 공간과 같은 충분한 리소스와 같은 외부 전제 조건이나 불변성을보고하는 것입니다. 이 경우 일반적으로 프로그램 또는 주요 하위 섹션을 종료하며 예외는 문제에 대한 정보를 전송하는 좋은 방법입니다. C ++ 예외는 프로그램이 계속되는 것을 방해하는 오류를보고하도록 설계되었습니다 .

C ++를 포함하여 대부분의 현대 언어에서 사용되는 예외 처리 모델은 고장난 것으로 알려져 있습니다. 너무 강력합니다. 이론가들은 이제 완전히 개방 된 "무엇이든 던지십시오"및 "아마도 잡을 수없는"모델보다 더 나은 모델을 개발했습니다. 또한 유형 정보를 사용하여 예외를 분류하는 것은 그리 좋은 생각이 아닙니다.

당신이 할 수있는 가장 좋은 방법입니다 그래서 드물게 던져 예외를 할 때 거기에 실제 오류, 그리고 그것을 다루는 다른 방법이 없을 때가능한 한 던져 지점에 가까이 캐치 예외 .


15
"예외 처리가 깨진 것으로 알려져 있습니다"및 "이론가들이 이제 더 나은 모델을 개발했습니다"에 대한 링크 / 참조를 추가해 주시겠습니까?
Juraj Blaho 2010

1
예외를 잡을 때 종종 알아야 할 중요한 사항은 대부분의 예외가 유용한 데이터를 제공하지 않는 경우 예외를 발생시킨 코드가 시스템 상태에 영향을 미쳤는지 여부입니다. 이론가의 모델이 이것을 다룹니까?
supercat 2011 년

@JurajBlaho 나는 성공하지 않고 당신이 말하는 링크 / 참조를 찾으려고 노력했습니다. 그의 대답을 편집하여 끝에 추가 할 수 있습니까?
ForceMagic

예 # 1과 # 3에 동의합니다. 그러나 코드가 너무 복잡해서 예외를 던지는 것에 대한 생각이 있다면 리팩토링이 필요할 것이라고 생각합니다. Assert는 코드가 제대로 실행되고 있는지 그리고 실제로 예외보다 비용이 적게 드는지 확인하는 좋은 방법 (더 나은 방법)이기도합니다. 나는이 주제에 관심이있는 모든 사람들에게 책 : The Pragmatic Programmer에서 2 개의 장 (Assertive Programming and When to use Exceptions)을 추천합니다.
ForceMagic

1
링크가 필요 없습니다. 두뇌를 사용하십시오. 잡히지 않은 예외를 던질 수 있습니다. 그 나쁜. goto는 적어도 항상 어딘가에 가기 때문에 goto보다 더 나쁩니다. 정적 유형 지정은 예외 사양에 대처할 수 없습니다. 유형 변수의 특정 인스턴스에 종속되기 때문에 다형성 함수가 예외 사양을 가질 수있는 정상적인 시스템은 없습니다. 새로운 C ++ 표준에서 제거되었습니다. 새로운 모델은 "분리 된 연속 요청"이라고 : en.wikipedia.org/wiki/Delimited_continuation
Yttrill

4

문제가 자신의 잘못된 코드에서 비롯된 경우 ASSERT를 사용하여이를 방지하는 것이 좋습니다. 사용자가 처리 할 수 ​​있기 때문에 프로그램이 처리 할 수없는 문제를 식별하고 사용자에 대해 알리려면 예외 처리가 필요합니다. 그러나 프로그램의 버그는 사용자가 처리 할 수있는 것이 아니므로 프로그램 충돌은

나는 받아 들여진 대답 의이 측면에 동의하지 않는다 . 어설 션은 예외를 던지는 것보다 낫지 않습니다. 예외가 런타임 오류 (또는 "외부 문제")에만 적합하다면 무엇을 std::logic_error위한 것입니까?

논리 오류는 거의 정의상 프로그램이 계속되는 것을 방해하는 종류의 조건입니다. 프로그램이 논리 구조이고 해당 논리 영역 외부에서 조건이 발생하면 어떻게 계속할 수 있습니까? 할 수있는 동안 입력을 모으고 예외를 던지십시오!

선행 기술이없는 것과는 다릅니다. std::vector, 이름은 하나이지만 논리 오류 예외, 즉 std::out_of_range. 표준 라이브러리를 사용하고 표준 예외를 잡을 수있는 최상위 핸들러가없는 경우 ( what () 을 호출 하고 (3)을 종료 하는 경우에만 ) 프로그램이 갑작스럽게 자동 종료됩니다.

assert 매크로는 훨씬 약한 가드입니다. 회복이 없습니다. 즉, 디버그 빌드를 실행하지 않는 경우가 아니면 실행없습니다 . assert 매크로는 계산이 오늘날보다 6 배 더 느린 시대에 속합니다. 논리 오류를 테스트하는 데 어려움을 겪고 있지만 그 테스트가 중요 할 때 사용하지 않는 경우 프로덕션에서 코드에 대해 많은 확신을 갖는 것이 좋습니다!

표준 라이브러리는 논리 오류 예외를 제공하고이를 사용합니다. 이유는 논리 오류가 발생하고 예외적이기 때문입니다. C 기능 어설 션은 예외가 작업을 훨씬 더 잘 처리 할 때 그러한 원시적 (그리고 아마도 쓸모없는) 메커니즘에 의존 할 이유가 없기 때문입니다.


3

이것에 대한 최고의 읽기

예외 처리는 지난 10 년 반 동안 많은 논의가있었습니다. 그러나 예외를 올바르게 처리하는 방법에 대한 일반적인 합의에도 불구하고 사용에 대한 차이는 계속 존재합니다. 부적절한 예외 처리는 발견하기 쉽고 피하기 쉬우 며 단순한 코드 (및 개발자) 품질 메트릭입니다. 절대적인 규칙이 밀접하거나 과장되어 있다는 것을 알고 있지만 일반적으로 try / catch를 사용해서는 안됩니다.

http://codebetter.com/karlseguin/2010/01/25/don-t-use-try-catch/


3
그 기사가 try/를 사용하지 마십시오 catch {}.
Craig McQueen

2

1. 결과로 예외가 발생할 가능성이 있거나 문제 사이 어딘가에 예외 검사가 코드에 포함됩니다.

2. try-catch 블록은 필요한 경우에만 사용하십시오. 각 try-catch 블록을 사용하면 코드의 최적화를 확실히 줄이는 추가 조건 검사가 추가됩니다.

3. _try_except가 유효한 변수 이름이라고 생각합니다 ....


3
FYI, __try 및 __except는 Microsoft의 SEH (구조적 예외 처리) 용입니다. msdn.microsoft.com/en-us/library/s58ftw19(v=vs.80).aspx
axw

0

기본적인 차이점은 다음과 같습니다.

  1. 하나는 당신을 위해 오류 처리를 만듭니다.
  2. 하나는 당신 자신입니다.

    • 예를 들어, 당신은 0 divide error. try catch 1.를 사용 하면 오류가 발생했을 때 도움이됩니다. 또는 당신 if a==0 then..2.

    • 예외를 잡으려고하지 않으면 더 빠르다고 생각하지 않습니다. 단순히 우회 하면 외부 처리기 errorthrew넘어갑니다.

스스로를 건네는 것은 문제가 더 이상 진행되지 않고 많은 경우에 속도면에서 이점이 있다는 것을 의미하지만 항상 그런 것은 아닙니다.

제안 : 단순하고 논리적으로 자신을 처리하십시오.


-3

많은 C / C ++ 순수 주의자들은 예외를 완전히 권장하지 않습니다. 주요 비판은 다음과 같습니다.

  1. 느립니다. 물론 정말 "느린"것은 아닙니다. 그러나 순수한 c / c ++에 비해 상당한 오버 헤드가 있습니다.
  2. 버그를 소개합니다. 예외를 제대로 처리하지 않으면 예외를 발생시키는 함수에서 정리 코드를 놓칠 수 있습니다.

대신 함수를 호출 할 때마다 반환 값 / 오류 코드를 확인하십시오.


15
무의미한 말. 첫째, 우리는 C ++에 대해 이야기하고 있습니다. "C / C ++"는 없으며 물론 C 전용 프로그래머는 예외로 인해 불편할 것입니다. "순수 C / C ++"는 존재하지 않으며 예외는 "순수 C ++"의 일부입니다. 표준에 있습니다. 버그가있는 예외 처리 코드를 작성할 수 있고 버그가있는 오류 반환 코드 또는 버그가있는 오류 상태 코드를 작성할 수 있습니다. 일반적으로 오류가 발생하기 쉬운 것으로 예외를 특성화하는 것은 오해의 소지가 있습니다. 죄송합니다, 그러나 이것은 내가 SO에서 본 최악의 조언의 일부이다
토니 델로이

1
원하는 경우 저를 표시하십시오. 그러나 C 배경에서 왔기 때문에 저와 많은 다른 사람들이 예외를 아예 사용하지 않는다고 말할 수 있습니다.
speedplane

4
1. 실제로 예외가 발생했을 때만 느립니다. 예외는 예외적 상황에서만 발생하기 때문에 예외라고합니다. 초당 9000 개 이상의 예외를 던지는 프로그램이 잘못하고 있습니다. 2. 순수 new-delete로 메모리 관리를하지 않도록주의하면 정리가 자동으로 수행됩니다. C 스타일 메모리 관리는 예외보다 훨씬 더 오류가 발생하기 쉽습니다. 3. 반환 값을 확인하면 코드 줄이 많이 추가되므로 읽기 및 유지 관리가 어려워집니다.
Septagram 2010

3
1) 컴파일러에 따라 사실이 아닐 수 있습니다. 그리고 모든 예외는 많은 양의 바이트 코드를 추가하여 실행 파일 크기를 늘리고 작업 속도를 늦 춥니 다. 2) 순수 new-delete는 항상 사용되며 모든 것이 스택 객체가 될 수는 없습니다. 3) 반환 값을 확인하는 것은 코드 줄이 더 많을 수 있지만 유지 관리가 더 쉽습니다. 동의하지 않을 수도 있지만 C ++ 예외가 많은 경우에 나쁘다는 것은 일반적으로 받아 들여지는 합리적인 믿음입니다. Embedded-C ++를 예로 들어 보겠습니다.
speedplane 2010

11 개의 반대표와 8 개의 찬성표. 이 의견이 일반적인 합의를 반영하지는 않지만 내가 설명한 이유 때문에 예외에 대해 경계하는 개발자가 많다는 것이 분명하다고 생각합니다.
스피드 플레인
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.