예외적으로 안전한 코드를 작성합니까? [닫은]


312

예외 처리 (EH)가 현재 표준 인 것처럼 보이며 웹을 검색하여 개선하거나 대체하려고 시도하는 소설 아이디어 나 방법을 찾을 수 없습니다 (일부 변형이 있지만 소설은 없습니다).

대부분의 사람들이 그것을 무시하거나 그냥 받아들이는 것처럼 보이지만 EH 에는 몇 가지 단점이 있습니다. 예외는 코드에 보이지 않으며 가능한 많은 출구 점을 만듭니다. 소프트웨어에 관한 Joel 은 그것에 관한 기사를 썼습니다 . goto완벽하게 비교하면 EH에 대해 다시 생각하게되었습니다.

EH를 피하고 반환 값, 콜백 또는 목적에 맞는 것을 사용하려고합니다. 그러나 신뢰할 수있는 코드를 작성해야 할 때는 요즘 EH를 무시할 수 없습니다 . 이는으로 시작하여 new0을 반환하는 대신 예외를 던질 수 있습니다 (예전과 같이). 이로 인해 모든 C ++ 코드 행이 예외에 취약 해집니다. 그리고 C ++ 기본 코드의 더 많은 곳에서 예외가 발생합니다 ... std lib가 수행합니다.

흔들리는 땅 위를 걷는 것 같은 느낌이 듭니다 .. 이제 우리는 예외에 대해주의를 기울여야합니다!

하지만 힘들어요 정말 힘들어요 예외 안전 코드 작성법을 배워야하며, 경험이 있어도 한 줄의 코드를 다시 확인해야 안전합니다! 또는 try / catch 블록을 어디에나 배치하기 시작하여 읽을 수없는 상태에 도달 할 때까지 코드를 복잡하게 만듭니다.

EH는 몇 가지 이해하기 쉽고 쉽게 해결할 수있는 결점을 가진 기존의 깨끗하고 결정적인 접근 방식 (반환 값 ..)을 코드에 여러 가지 가능한 종료점을 생성하고 예외를 포착하는 코드 작성을 시작하는 경우 ( 특정 시점에서 강제로 수행되면 코드를 통해 여러 경로를 생성합니다 (캐치 블록의 코드, std :: cerr .. 이외의 로깅 기능이 필요한 서버 프로그램에 대해 생각하십시오). EH에는 장점이 있지만, 요점이 아닙니다.

내 실제 질문 :

  • 실제로 예외 안전 코드를 작성합니까?
  • 마지막 "제작 준비"코드가 예외 안전하다고 확신하십니까?
  • 확신 할 수 있습니까?
  • 작동하는 대안을 알고 있거나 실제로 사용하고 있습니까?

44
"EH는 기존의 깨끗한 결정적 접근 방식을 대체했습니다 (반환 값 ..)"무엇? 예외는 리턴 코드와 마찬가지로 결정적입니다. 그들에 대해 무작위는 없습니다.
rlbond 2009

2
EH는 오랜 기간 동안 표준으로 사용되었습니다 (Ada부터!)

12
"EH"는 예외 처리를위한 축약 형입니까? 나는 전에 그것을 본 적이 없다.
Thomas Padron-McCarthy

3
뉴턴의 법은 오늘날 대부분의 것들에 적용됩니다. 그들이 수세기 동안 매우 광범위한 현상을 예측했다는 사실은 중요합니다.
David Thornley

4
@ frunsi : 헷갈 리게해서 죄송합니다! 그러나 나는 이상하고 설명 할 수없는 약어 또는 약어가 사람들을 실망시키지 않을 것이라고 생각합니다.
Thomas Padron-McCarthy

답변:


520

귀하의 질문은 "예외 안전 코드 작성이 매우 어렵다"고 주장합니다. 먼저 질문에 답한 다음 숨겨진 질문에 답하겠습니다.

질문에 대답

실제로 예외 안전 코드를 작성합니까?

물론입니다.

이다 자바는 C ++ 프로그래머 (RAII 의미의 부족)으로 나에게 그것의 호소를 많이 잃은 이유, 그러나 나는 탈선 오전 : 이것은 C ++ 질문이다.

실제로 STL 또는 Boost 코드로 작업해야 할 때 필요합니다. 예를 들어, C ++ 스레드 ( boost::thread또는 std::thread)는 예외를 throw하여 정상적으로 종료됩니다.

마지막 "제작 준비"코드가 예외 안전하다고 확신하십니까?

확신 할 수 있습니까?

예외 안전 코드 작성은 버그없는 코드 작성과 같습니다.

코드가 예외 안전하다는 것을 100 % 확신 할 수는 없습니다. 그러나 잘 알려진 패턴을 사용하고 잘 알려진 반 패턴을 피하려고 노력합니다.

작동하는 대안을 알고 있거나 실제로 사용하고 있습니까?

C ++ 에는 실행 가능한 대안 이 없습니다 (즉, C로 되돌리고 C ++ 라이브러리와 Windows SEH와 같은 외부 놀라움을 피해야 함).

예외 안전 코드 작성

예외 안전 코드를 작성하려면 먼저 각 명령이 작성하는 예외 안전 수준을 알아야합니다 .

예를 들어, new는 예외를 던질 수 있지만 내장 (예 : int 또는 포인터)을 할당해도 실패하지 않습니다. 스왑은 절대 실패하지 않습니다 (투척 스왑을 쓰지 마십시오) std::list::push_back.

예외 보증

가장 먼저 이해해야 할 것은 모든 기능이 제공하는 예외 보증을 평가할 수 있어야한다는 것입니다.

  1. none : 귀하의 코드는 절대로 그것을 제공해서는 안됩니다. 이 코드는 모든 것을 유출하고 가장 먼저 발생하는 예외를 분석합니다.
  2. 기본 : 이것은 최소한 제공해야 할 보증입니다. 즉, 예외가 발생하면 리소스가 유출되지 않고 모든 객체가 여전히 전체입니다.
  3. strong : 처리가 성공하거나 예외가 발생하지만 처리가 시작되면 처리가 전혀 시작되지 않은 것과 동일한 상태가됩니다 (이는 C ++에 트랜잭션 성능을 제공합니다).
  4. nothrow / nofail : 처리가 성공합니다.

코드 예

다음 코드는 올바른 C ++처럼 보이지만 실제로는 "없음"보증을 제공하므로 올바르지 않습니다.

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   X * x = new X() ;                // 2. basic : can throw with new and X constructor
   t.list.push_back(x) ;            // 3. strong : can throw
   x->doSomethingThatCanThrow() ;   // 4. basic : can throw
}

이런 종류의 분석을 염두에두고 모든 코드를 작성합니다.

제공되는 최저 보증은 기본이지만 각 명령의 순서는 전체 기능을 "없음"으로 만듭니다. 3. throw가 발생하면 x가 누출되기 때문입니다.

가장 먼저 할 일은 "기본"기능을 만드는 것입니다. 즉, x가 목록에서 안전하게 소유 할 때까지 x를 스마트 포인터에 넣습니다.

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   std::auto_ptr<X> x(new X()) ;    // 2.  basic : can throw with new and X constructor
   X * px = x.get() ;               // 2'. nothrow/nofail
   t.list.push_back(px) ;           // 3.  strong : can throw
   x.release() ;                    // 3'. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 4.  basic : can throw
}

이제 코드는 "기본"보증을 제공합니다. 누수가 없으며 모든 객체가 올바른 상태가됩니다. 그러나 우리는 더 많은 것, 즉 강력한 보증을 제공 할 수 있습니다. 이것은 비용이 많이들 있는 곳이므로 모든 C ++ 코드가 강력 하지는 않습니다 . 해 봅시다:

void doSomething(T & t)
{
   // we create "x"
   std::auto_ptr<X> x(new X()) ;    // 1. basic : can throw with new and X constructor
   X * px = x.get() ;               // 2. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 3. basic : can throw

   // we copy the original container to avoid changing it
   T t2(t) ;                        // 4. strong : can throw with T copy-constructor

   // we put "x" in the copied container
   t2.list.push_back(px) ;          // 5. strong : can throw
   x.release() ;                    // 6. nothrow/nofail
   if(std::numeric_limits<int>::max() > t2.integer)  // 7.   nothrow/nofail
      t2.integer += 1 ;                              // 7'.  nothrow/nofail

   // we swap both containers
   t.swap(t2) ;                     // 8. nothrow/nofail
}

우리는 작업을 재정렬하여 먼저 X올바른 가치를 창출하고 설정 합니다. 조작이 실패하면 t수정되지 않으므로 조작 1-3 은 "강력한"것으로 간주 될 수 있습니다. 무언가가 발생하면 t수정 X되지 않으며 스마트 포인터가 소유하고 있기 때문에 누출되지 않습니다.

그런 다음의 사본 t2을 작성하고 t조작 4에서 7까지이 사본에 대해 작업합니다. 무언가가 발생하면 t2수정되었지만 t여전히 원본입니다. 우리는 여전히 강력한 보증을 제공합니다.

그런 다음, 우리는 교환 tt2. 스왑 작업은 C ++에서 무시해야하므로 작성한 스왑 T이 nothrow (아니면 그렇지 않으면 다시 작성하십시오)가 되기를 바랍니다 .

따라서 함수의 끝에 도달하면 모든 것이 성공하고 (반환 유형이 필요하지 않음) t예외 값이 있습니다. 실패하면 t여전히 원래 값을 갖습니다.

이제 강력한 보증을 제공하는 것은 비용이 많이들 수 있으므로 모든 코드에 강력한 보증을 제공하려고 노력하지 마십시오. 그러나 비용없이 할 수 있다면 (그리고 C ++ 인라인 및 기타 최적화로 모든 코드를 비용 효율적으로 만들 수 있습니다) 그런 다음 해보십시오. 기능 사용자가 감사합니다.

결론

예외 안전 코드를 작성하는 습관이 필요합니다. 사용할 각 지침에서 제공하는 보증을 평가 한 다음 지침 목록에서 제공하는 보증을 평가해야합니다.

물론 C ++ 컴파일러는 보증을 백업하지 않지만 (내 코드에서는 @warning doxygen 태그로 보증을 제공합니다) 다소 슬픈 일이지만 예외 안전 코드를 작성하지 못하게해서는 안됩니다.

정상적인 실패와 버그

프로그래머는 실패없는 기능이 항상 성공할 것이라고 어떻게 보장 할 수 있습니까? 결국 함수에 버그가있을 수 있습니다.

사실입니다. 예외 보증은 버그가없는 코드로 제공됩니다. 그러나 모든 언어에서 함수를 호출하면 함수에 버그가 없다고 가정합니다. 제정신의 코드는 버그가있을 가능성으로부터 스스로를 보호하지 않습니다. 코드를 최대한 작성하고 버그가 없다고 가정하여 보증을 제공하십시오. 그리고 버그가 있으면 수정하십시오.

예외는 코드 버그가 아닌 예외적 인 처리 실패에 대한 것입니다.

마지막 말

이제 문제는 "이것이 가치가 있습니까?"입니다.

당연하지. 실패하지 않을 것이라는 것을 알고있는 "Nothrow / no-fail"기능을 갖는 것은 큰 혜택입니다. 커밋 / 롤백 기능이있는 데이터베이스와 같은 트랜잭션 의미론으로 코드를 작성할 수있게하는 "강력한"기능에 대해서도 마찬가지입니다.

그런 다음 "기본"은 제공해야 할 최소한의 보증입니다. C ++은 범위가 매우 강력한 언어이므로 자원 누출을 피할 수 있습니다 (가비지 수집기가 데이터베이스, 연결 또는 파일 핸들에 제공하기 어려운 것).

그래서, 지금까지의 내가 그것을보고, 그것은 이다 가치.

2010-01-29 편집 : 비 투척 스왑 정보

nobar는 "예외 안전 코드를 작성하는 방법"의 일부이기 때문에 관련성이 있다고 생각합니다.

  • [me] 스왑은 절대 실패하지 않습니다 (투척 스왑도 쓰지 마십시오)
  • [nobar] 사용자 정의 작성 swap()기능에 대한 권장 사항입니다 . 그러나 std::swap()내부적으로 사용하는 작업에 따라 실패 할 수 있습니다.

기본값 std::swap은 일부 객체의 경우 복사 및 할당을 수행하여 복사 할 수 있습니다. 따라서 클래스 또는 STL 클래스에 사용되는 기본 스왑이 발생할 수 있습니다. 지금까지 ++ 표준에 관한 한 C로, 스왑 동작은 vector, deque그리고 list이 수에 대한 반면, 포기하지 않습니다 map비교 펑은 (참조 복사 건설에 던질 수있는 경우 는 C ++ 언어, 스페셜 에디션, 부록 E, E.4.3 프로그래밍 . 스왑 ).

벡터 스왑의 Visual C ++ 2008 구현을 살펴보면 두 벡터가 동일한 할당 자 (즉, 일반적인 경우)를 가진 경우 벡터 스왑은 발생하지 않지만 할당자가 다른 경우 복사를 수행합니다. 따라서이 마지막 경우에 던질 수 있다고 가정합니다.

따라서 원본 텍스트는 여전히 유지합니다. 던지는 스왑을 작성하지 마십시오. 그러나 nobar의 주석은 기억해야합니다. 스왑하는 객체에 던지지 않는 스왑이 있는지 확인하십시오.

2011-11-06 : 흥미로운 기사 편집

STL 예외를 안전하게 만드는 것에 대한 그의 기사에 기술 된 기본 / 강력 / 무질서 보장 을 제공 한 Dave Abrahams

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

7 번째 포인트 (예외 안전을위한 자동화 된 테스트)를 살펴보면 모든 사례를 테스트하기 위해 자동화 된 단위 테스트를 사용합니다. 나는이 부분이 질문 저자의 " 당신도 확신 할 수 있습니까? "에 대한 훌륭한 해답이라고 생각합니다 .

2013 년 5 월 31 일 편집 : dionadar의 의견

t.integer += 1;오버플로가 예외적으로 안전하지 않다는 보장이 없으며 실제로 기술적으로 UB를 호출 할 수 있습니다! 부호있는 오버플로는 UB : C ++ 11 5/4 "표현식을 평가하는 동안 결과가 수학적으로 정의되지 않거나 해당 유형의 표현 가능한 값 범위에 있지 않으면 동작이 정의되지 않습니다." 정수는 오버플로되지 않지만 동등 클래스 modulo 2 ^ # bits에서 계산을 수행합니다.

Dionadar는 실제로 다음과 같은 행을 언급하고 있으며 실제로 정의되지 않은 동작이 있습니다.

   t.integer += 1 ;                 // 1. nothrow/nofail

여기서 해결책 std::numeric_limits<T>::max()은 추가하기 전에 정수가 이미 최대 값에 있는지 확인하는 것입니다 (를 사용하여 ).

내 오류는 "정상적인 오류 대 버그"섹션, 즉 버그입니다. 그것은 추론을 무효화하지 않으며, 예외 안전 코드가 달성 불가능하기 때문에 쓸모가 없다는 것을 의미하지는 않습니다. 컴퓨터 전원을 끄거나 컴파일러 버그, 심지어 버그 또는 기타 오류로부터 자신을 보호 할 수 없습니다. 완벽을 얻을 수는 없지만 가능한 한 가까워 지려고 노력할 수 있습니다.

Dionadar의 의견을 염두에두고 코드를 수정했습니다.


6
고마워요! 나는 여전히 다른 것을 원했다. 그러나 C ++을 고수하고 자세한 설명을 해준 것에 대한 귀하의 답변을 수락합니다. 당신은 심지어 "이것으로 인해 C ++ 코드가 예외에 취약하다"고 반박했다. 이 라인 별 분석은 결국 의미가 있습니다 ... 나는 그것에 대해 생각해야합니다.
Frunsi 2009

8
"스왑은 절대 실패하지 않습니다". 이것은 사용자 정의로 작성된 swap () 함수에 권장됩니다. 그러나 std :: swap ()은 내부적으로 사용하는 작업에 따라 실패 할 수 있습니다.
nobar

7
@Mehrdad : "finally" does exactly what you were trying to achieve: 물론 그렇습니다 . 그리고을 사용하여 취성 코드를 생성하기 쉽기 때문에 finally"자원을 사용하여 시도"개념이 마침내 Java 7 (C # 10 년 후, usingC ++ 소멸자 30 년 후)에 도입되었습니다. 이것이 내가 비판하는 취성입니다. 에 관해서는 Just because [finally] doesn't match your taste (RAII doesn't match mine, [...]) doesn't mean it's "failing": 쓰레기 수집 언어 (C #을의 RAII 영감 문을 추가하는 경향이에서, 업계, 당신의 취향에 동의 using하고 자바를 try).
paercebal

5
@Mehrdad : RAII doesn't match mine, since it needs a new struct every single darn time, which is tedious sometimes: 아니, 그것은하지 않습니다. 스마트 포인터 또는 유틸리티 클래스를 사용하여 리소스를 "보호"할 수 있습니다.
paercebal

5
@Mehrdad : "래퍼가 필요하지 않은 무언가를 위해 래퍼를 만들도록 강요합니다"-RAII 방식으로 코딩 할 때 어떤 종류의 래퍼도 필요하지 않습니다. 리소스는 개체이며 개체 수명은 리소스 수명입니다. 그러나 저는 C ++ 세계에서 왔으며 현재 Java 프로젝트로 어려움을 겪고 있습니다. C ++의 RAII와 비교할 때 Java의 "수동 자원 관리"는 필수입니다! 나의 의견이지만 Java는 오래 전에 "자동 리소스 관리"를 "자동 메모리 관리"로 바꿨습니다.
Frunsi

32

C ++에서 예외 안전 코드를 작성하는 것은 많은 try {} catch {} 블록을 사용하는 것과 관련이 없습니다. 코드가 어떤 종류의 보증을 제공하는지 문서화하는 것입니다.

Herb Sutter의 Guru Of The Week 시리즈, 특히 할부 59, 60 및 61을 읽는 것이 좋습니다 .

요약하면 세 가지 수준의 예외 안전을 제공 할 수 있습니다.

  • 기본 : 코드에서 예외가 발생하면 코드에서 리소스가 누출되지 않으며 객체는 계속 파괴 가능합니다.
  • 강함 : 코드에서 예외가 발생하면 응용 프로그램의 상태는 변경되지 않습니다.
  • 던지지 않음 : 코드에서 예외가 발생하지 않습니다.

개인적으로, 나는이 기사를 꽤 늦게 발견했기 때문에 많은 C ++ 코드는 확실히 예외 안전하지 않습니다.


1
그의 책 "Exceptional C ++"도 잘 읽었습니다. 하지만 난 여전히 ... EH의 개념에 의문을 제기하려고
Frunsi

8
+1 OP는 예외 안전 (일반적으로 RAII에 대한 추가 정보)
jk를 사용

프로덕션 C ++ 코드가 거의없는 것이 예외 안전하다고 생각합니다.
Raedwald

18

우리 중 일부는 20 년 이상 예외를 사용해 왔습니다. 예를 들어 PL / I가 있습니다. 그들이 새롭고 위험한 기술이라는 전제는 의문의 여지가 있습니다.


EH에 의문을 제기하고 있습니다. 그리고 특히 C ++ EH. 그리고 대안을 찾고 있습니다. 어쩌면 나는 그것을 받아 들여야 할 것입니다 (그리고 유일한 방법이라면 내가 할 것입니다), 그러나 더 나은 대안이있을 수 있다고 생각합니다. 이 개념이 새로운 것은 아니라고 생각하지만, 그래도 리턴 코드를 사용하여 명시적인 오류 처리보다 더 위험 수 있다고 생각합니다 ...
Frunsi

1
마음에 들지 않으면 사용하지 마십시오. 던질 수있는 호출 주위에 블록을 배치하고, 예전의 에러 코드를 재발 명하고, 어떤 경우에는 용인 할 수있는 문제로 어려움을 겪습니다.
bmargulies 2009

좋아, 완벽 해, 나는 단지 EH와 에러 코드를 사용하고 그것과 함께 살 것이다. 나는 바보입니다, 나는 그 솔루션에 내 스스로 왔어 야했다! ;)
Frunsi 2009

17

우선 Neil이 언급했듯이 SEH는 Microsoft의 구조적 예외 처리입니다. C ++의 예외 처리와 유사하지만 동일하지는 않습니다. 실제로 Visual Studio에서 C ++ 예외 처리 를 원할 경우 C ++ 예외 처리활성화 해야 합니다. 기본 동작으로 인해 로컬 개체가 모든 경우에 파괴되지는 않습니다. 두 경우 모두, 예외 처리는 정말 아니다 어려워 그냥이다 다른 .

이제 실제 질문에 대해

실제로 예외 안전 코드를 작성합니까?

예. 모든 경우에 예외 안전 코드를 찾으려고 노력합니다. 리소스에 대한 범위가 지정된 액세스 (예 : boost::shared_ptr메모리, boost::lock_guard잠금)에 RAII 기술을 사용하여 전파 합니다. 일반적으로 RAII범위 보호 기술을 일관되게 사용 하면 예외 안전 코드를 훨씬 쉽게 작성할 수 있습니다. 요령은 존재하는 것과 그것을 적용하는 방법을 배우는 것입니다.

마지막 "제작 준비"코드가 예외 안전하다고 확신하십니까?

아니요. 그대로 안전합니다. 나는 몇 년 동안 24/7 활동의 예외로 인해 프로세스 결함을 보지 못했다고 말할 수 있습니다. 나는 완벽한 코드를 기대하지 않고 잘 작성된 코드를 기대합니다. 예외 안전을 제공하는 것 외에도 위의 기술은 try/ catch블록 으로 달성하기가 거의 불가능한 정확성을 보장합니다 . 최상위 제어 범위 (스레드, 프로세스 등)의 모든 것을 포착하는 경우 예외에 직면하여 ( 대부분의 경우 ) 계속 실행할 수 있습니다 . 같은 기술 또한 계속 실행 도움이 될 것입니다 제대로 예외에 직면 하지 않고 try/ catch사방 블록 .

당신은 그것을 확신 할 수 있습니까?

예. 철저한 코드 감사로 확신 할 수 있지만 실제로는 그렇게하지 않습니다. 정기적 인 코드 검토와 신중한 개발자는 먼 길을갑니다.

작동하는 대안을 알고 있거나 실제로 사용하고 있습니까?

상위 비트 (ala HRESULTs ) 인코딩 상태 또는 그 끔찍한 핵과 같은 몇 년 동안 몇 가지 변형을 시도했습니다 setjmp() ... longjmp(). 이 두 가지 방법은 실제로 완전히 다른 방식으로 분류됩니다.


결국 몇 가지 기술을 적용하고 실제로 예외에 대한 응답으로 무언가를 수행 할 수있는 위치에 대해 신중하게 생각하는 습관을 들이게되면 예외적으로 안전한 매우 읽기 쉬운 코드가 생깁니다. 다음 규칙에 따라이를 요약 할 수 있습니다.

  • 당신은보고 싶은 try/ catch특정 예외에 대해 뭔가를 할 수있을 때
  • 당신은 거의 원시 new또는 delete코드 를보고 싶지 않습니다.
  • 피하다 std::sprintf , snprintf일반적으로, 및 배열 - 사용 std::ostringstream하여 배열을 포맷 및 교체에 대한 std::vectorstd::string
  • 의심스러운 경우 직접 롤링하기 전에 Boost 또는 STL의 기능을 찾으십시오.

C ++로 작성하려는 경우 예외를 올바르게 사용하는 방법을 배우고 결과 코드를 잊어 버릴 것을 권장합니다. 당신이 예외를 피하려면, 당신은 하나가 또 다른 언어로 작성하는 것을 고려할 수 있습니다 을하지 않습니다 또는 그들이 안전합니다 . C ++을 완전히 활용하는 방법을 배우고 싶다면 Herb Sutter , Nicolai JosuttisScott Meyers 의 몇 권의 책을 읽으십시오 .


"기본 동작은 모든 경우에 로컬 개체가 소멸되는 것을 보장하지는 않습니다."Visual Studio C ++ 컴파일러의 기본 설정 은 예외 상황에서 잘못된 코드를 생성한다고 합니다. 정말 그래요?
Raedwald

"원 new시나 delete코드 로보고 싶지 않다 ": " 원시적으로 는 생성 자나 소멸자 밖에있는 것 같아요."
Raedwald

@Raedwald-re : VC ++ : VC ++의 VS2005 에디션은 SEH 예외가 발생해도 로컬 객체를 파괴하지 않습니다. "C ++ 예외 처리 사용"을 읽으십시오 . VS2005에서 SEH 예외는 기본적으로 C ++ 객체의 소멸자를 호출하지 않습니다. Win32 함수 나 C- 인터페이스 DLL에 정의 된 것을 호출하면 SEH 예외를 발생시킬 수 있기 때문에 걱정할 필요가 있습니다.
D.Shawley

@Raedwald : re : raw : 기본적으로 delete구현 등의 외부에서 사용해서는 안됩니다 tr1::shared_ptr. new사용법이 다음과 같은 경우 사용할 수 있습니다 tr1::shared_ptr<X> ptr(new X(arg, arg));. 중요한 부분은 결과가 new관리되는 포인터로 직접 이동한다는 것입니다. 페이지의 boost::shared_ptr모범 사례 최선을 설명합니다.
D.Shawley

예외 안전 규칙 세트에서 std :: sprintf (et al)를 왜 참조합니까? 예를 들어 en.cppreference.com/w/cpp/io/c/fprintf
mabraham

10

"모든 라인이 던져 질 수있다"는 가정하에 예외 안전 코드를 작성할 수 없습니다. 예외 안전 코드의 설계는 코드에서 예상, 관찰, 준수 및 이행해야하는 특정 계약 / 보증인에게 매우 중요합니다. 절대로 보장 되지 않는 코드가 있어야합니다. 던져 . 거기에는 다른 종류의 예외 보장이 있습니다.

다시 말해, 예외 안전 코드를 작성 하는 것은 단순한 코딩 문제가 아니라 프로그램 설계 문제입니다 .


8
  • 실제로 예외 안전 코드를 작성합니까?

글쎄, 나는 확실히하고자한다.

  • 마지막 "제작 준비"코드가 예외 안전하다고 확신하십니까?

예외를 사용하여 작성된 24/7 서버는 24/7로 실행되며 메모리가 누출되지 않습니다.

  • 확신 할 수 있습니까?

모든 코드가 올바른지 확인하기는 매우 어렵습니다. 일반적으로 결과에 의해서만 갈 수 있습니다

  • 작동하는 대안을 알고 있거나 실제로 사용하고 있습니까?

아니요. 예외를 사용하는 것은 지난 30 년 동안 프로그래밍에서 사용한 대안보다 깨끗하고 쉽습니다.


30
이 답변은 가치가 없습니다.
Matt Joiner

6
@MattJoiner 그러면 질문에는 가치가 없습니다.
Miles Rout

5

SEH와 C ++ 예외 사이의 혼동을 피하면서 언제든지 예외가 발생할 수 있음을 알고이를 염두에두고 코드를 작성해야합니다. 예외 안전에 대한 요구는 RAII, 스마트 포인터 및 기타 최신 C ++ 기술의 사용을 주도하는 것입니다.

잘 설정된 패턴을 따르는 경우 예외 안전 코드를 작성하는 것은 그리 어렵지 않으며 실제로 모든 경우에 오류 리턴을 올바르게 처리하는 코드를 작성하는 것이 더 쉽습니다.


3

일반적으로 EH가 좋습니다. 그러나 C ++의 구현은 예외 포착 범위가 얼마나 좋은지 말하기가 어렵 기 때문에 그리 친숙하지 않습니다. 예를 들어 Java는 이것을 쉽게 만들어줍니다. 가능한 예외를 처리하지 않으면 컴파일러가 실패하는 경향이 있습니다.


2
신중하게 사용하면 더 좋습니다 noexcept.
einpoklum

2

EH 핸들러가 없으면 편집기에서 오류가 발생하기 때문에 Eclipse 및 Java (Java에 익숙하지 않은) 작업을 정말로 좋아합니다. 그것은 예외를 처리하는 것을 잊어 버리는 것을 많이 만듭니다 ...

또한 IDE 도구를 사용하여 try / catch 블록 또는 다른 catch 블록을 자동으로 추가합니다.


5
이것이 checked (Java)와 unchecked (c ++) 예외의 차이점입니다 (Java에도 예외가 있습니다). 확인 된 예외의 장점은 작성한 것이지만 자체 단점이 있습니다. 차이점과 접근 방식에 대한 Google의 차이
David Rodríguez-dribeas

2

우리 중 일부는 Java와 같은 언어를 선호하므로 C ++ 및 C #에서와 같이 메소드에서 발생하는 모든 예외를 보이지 않게하는 대신 선언합니다.

호출 체인에 장애를 수동으로 전파 할 필요가없는 다른 이유가 없으면 예외가 오류 리턴 코드보다 우수합니다.

즉, 저수준 API 라이브러리 프로그래밍은 예외 처리를 피하고 오류 반환 코드를 고수해야합니다.

C ++에서 깨끗한 예외 처리 코드를 작성하는 것이 어려운 경험이 있습니다. 나는 new(nothrow)많이 사용하게됩니다.


4
그리고 당신은 대부분의 표준 라이브러리를 피하고 있습니까? 사용 new(std::nothrow)이 충분하지 않습니다. 그런데, 자바보다 C에서 쓰기 예외 안전 ++ 코드에 쉽게 : en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
avakar

3
Java에서 확인한 예외 사용성이 크게 과장되었습니다. 실제로 Java 이외의 언어는 성공으로 간주되지 않습니다. 그렇기 때문에 C ++의 "throw"문은 이제 더 이상 사용되지 않는 것으로 간주되며 C #에서는 구현을 심각하게 고려하지 않았습니다 (디자인 선택). 자바를 들어, 다음 문서는 enlightning 수 : googletesting.blogspot.com/2009/09/...
paercebal

2
C ++에서 예외 안전 코드를 작성하는 것이 그렇게 어렵지 않으며 일반적으로 코드가 더 깨끗 해지는 경향이 있습니다. 물론 그렇게하는 법을 배워야합니다.
David Thornley

2

나는 예외 안전 코드를 작성하기 위해 최선을 다합니다.

, 줄을 던질 수 있는 곳을 주시해야합니다 . 모든 사람이 할 수있는 것은 아니며,이를 명심하는 것이 매우 중요합니다. 핵심은 실제로 표준에 정의 된 예외 보장을 고려하고 코드를 충족하도록 코드를 설계하는 것입니다.

강력한 예외 보증을 제공하기 위해이 작업을 작성할 수 있습니까? 기본 사항을 정해야합니까? 어떤 줄에서 예외가 발생할 수 있으며, 그렇게하면 개체가 손상되지 않도록하려면 어떻게해야합니까?


2
  • 실제로 예외 안전 코드를 작성합니까? [그런 일은 없습니다. 관리되는 환경이 없으면 예외는 오류에 대한 종이 보호막입니다. 이것은 처음 세 질문에 적용됩니다.]

  • 작동하는 대안을 알고 있거나 실제로 사용하고 있습니까? [대체 무엇입니까? 여기서 문제는 사람들이 실제 오류를 정상적인 프로그램 작업과 분리하지 않는다는 것입니다. 정상적인 프로그램 작업 (예 : 파일을 찾을 수 없음) 인 경우 실제로 오류 처리가 아닙니다. 실제 오류 인 경우 '처리'할 방법이 없거나 실제 오류가 아닙니다. 여기서 문제는 무엇이 잘못되었는지 확인하고 스프레드 시트를 중지하고 오류를 기록하거나 드라이버를 토스터에 다시 시작하거나 소프트웨어가 버그가 있어도 최선을 다할 때에도 제트 전투기가 계속 비행 할 수 있도록기도하는 것입니다.]


0

많은 사람들 (내가 말을 가장 많이한다)

예외에 대해 정말로 중요한 것은 처리 코드를 작성하지 않으면 결과가 완벽하게 안전하고 올바르게 작동한다는 것입니다. 너무 당황하지만 안전합니다.

당신은 필요 안전하지 않은 것을 얻으려면 처리기에서 실수 적극적으로 catch (...) {} 만 오류 코드를 무시하는 것과 비교됩니다.


4
사실이 아니다. 예외 안전하지 않은 코드를 작성하는 것은 매우 쉽습니다. 예를 들어 : f = new foo(); f->doSomething(); delete f;doSomething 메소드에서 예외가 발생하면 메모리 누수가있는 것입니다.
Kristopher Johnson

1
프로그램 종료 시점은 중요하지 않습니다. 실행을 계속하려면 예외 를 적극적으로 삼켜야합니다. 정리없이 종료 할 수없는 특정한 경우가 있지만 이러한 상황은 모든 프로그래밍 언어와 스타일에 특별한주의가 필요합니다.
ima December

C ++이나 관리 코드에서는 예외를 무시할 수 없으며 처리 코드를 작성할 수 없습니다. 안전하지 않으며 제대로 작동하지 않습니다. 장난감 코드를 제외하고.
Frunsi 2009

1
응용 프로그램 코드에서 예외를 무시하면 외부 자원이 관련 될 때 프로그램이 여전히 제대로 작동하지 않을 수 있습니다. 사실, OS는 파일 핸들, 잠금, 소켓 등을 닫는 데 관심이 있습니다. 그러나 모든 것이 처리되는 것은 아닙니다. 예를 들어 쓸 때 불필요한 파일이 남거나 파일이 손상 될 수 있습니다. 예외를 무시하면 Java에 문제가 있고 C ++에서는 RAII로 작업 할 수 있습니다 (그러나 RAII 기술을 사용할 때는 예외에 관심이 있기 때문에 대부분 예외 를 사용합니다 ) ...
Frunsi

3
제 말을 왜곡하지 마십시오. 나는 "처리 코드를 작성하지 않으면"이라고 썼다. 예외를 무시하려면 코드를 작성해야합니다.
ima
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.