2012-03-28 3 views
2

강력한 예외 안전 보증은 예외가 발생할 경우 작업이 프로그램 상태를 변경하지 않는다고 말합니다. 예외 안전 복사 할당을 구현하는 우아한 방법은 copy-and-swap idiom입니다.C++ 예외 안전 편집증 : 얼마나 많은가?

내 질문은 :

  1. 은 비 기본 형식을 변이합니다 클래스의 모든 돌연변이 작동 복사 및 스왑을 사용하는 잔인한시겠습니까?

  2. 강력한 예외 안전을위한 성능은 실제로 공정한 거래입니까? 예를 들어

:.

그것은 당신의 응용 프로그램이 당신이 당신 자신의 컴퓨터에 (스펙트럼의 한쪽 끝을)를 실행하는 경우에 실행하는 데 무슨 일인지 환경에 따라
class A 
{ 
    public: 
     void increment() 
     { 
      // Copy 
      A tmp(*this); 

      // Perform throwing operations on the copy 
      ++(tmp.x); 
      tmp.x.crazyStuff(); 

      // Now that the operation is done sans exceptions, 
      // change program state 
      swap(tmp); 
     } 

     int setSomeProperty(int q) 
     { 
      A tmp(*this); 
      tmp.y.setProperty("q", q); 
      int rc = tmp.x.otherCrazyStuff(); 
      swap(tmp); 
      return rc; 
     } 

     // 
     // And many others similarly 
     // 

     void swap(const A &a) 
     { 
      // Non-throwing swap 
     } 

    private: 
     SomeClass x; 
     OtherClass y; 
}; 
+1

답변은 귀하의 도메인에 크게 달려 있으며 답변을받을 수 없습니다. 인생이 그것에 달려있다면 그 해답은 분명합니다. 그것은 성과 위약의 가치가 있습니다. 예외 처리 코드가 예외 발생시 개체를 파괴하면 문제가되지 않습니다. 이제 코드가 어디에 있는지 알아 내야합니다. –

+0

코드를 단일 책임 구성 요소에 올바르게 입력하면 "무료"로 많은 예외 안전성을 얻게됩니다. 당신의 예제에서, 왜 "증분"기능은 "미친 것들"을합니까? 증분 알고리즘이 복잡하고 트랜잭션을 계산하는 방법을 알고 있거나 증분 및 crazystuffing을 분리해야합니다. –

+0

같은 아이디어를 사용하여 회원에게 적용 할 수 있습니다. 전체가 될 필요는 없습니다. 클래스에 문자열 벡터가 포함되어 있고 그 중 하나를 변경해야한다면 어떻게 될까요? 전체 클래스, 벡터 및 전체를 복사/스왑하지 마십시오. 한 줄에 그냥 써라. – BoBTFish

답변

1

공학의 모든 문제와 마찬가지로, 그것은 균형에 관한 것입니다.

확실하게, const - 적합성/무결성 및 강력한 보장으로 인해 코드에 대한 신뢰도가 높아집니다 (특히 테스트가 수반 됨). 또한 버그에 대한 가능한 설명을 줄이는데도 도움이됩니다.

그러나 성능에 영향을 줄 수 있습니다.

모든 성능 문제와 마찬가지로, 나는 말할 것입니다 : 프로필 및 핫 스팟 제거. 복사 및 스왑은 이 아닙니다. 트랜잭션 의미를 구현하는 유일한 방법입니다 (가장 쉬운 방법입니다). 프로파일 링을 사용하면 절대 사용하지 말아야 할 위치를 알 수 있습니다. 대안을 찾아야합니다.

1

, 그것은 그렇지 않을 수도 예외 안전에 너무 엄격 할 가치가있다. 예를 들어 프로그램을 작성하는 중입니다. 의료 기기의 경우 (다른 쪽), 예외가 발생했을 때 의도하지 않은 부작용이 남지 않도록하십시오. 그 사이의 모든 것은 오류에 대한 허용 수준과 개발에 사용할 수있는 리소스 (시간, 돈 등)에 따라 다릅니다.

3

항상 기본 예외 보장을 목표로해야합니다. 예외 발생시, 모든 자원이 올 Y 르게 해제되고 오브젝트가 유효한 s ​​태 (미정도이지만 유효 할 수 있음)에 있습니다.

강력한 예외 보장 (즉, "거래")은 그것이 의미가 있다고 생각할 때 구현해야하는 것이므로 항상 트랜잭션 동작이 필요하지는 않습니다.

트랜잭션 작업 (예 : & 스왑 교체)을 통해 달성하기가 쉬운 경우에는 수행하십시오. 그러나 때로는 할당 연산자와 같은 기본적인 것들에 대해서조차도 그렇지 않거나 큰 성능 영향을 받게됩니다. boost : : variant와 같은 것을 구현 한 것을 기억합니다. 항상 사본 할당에서 강력한 보장을 제공하지 못했습니다.

당신이 직면하게 될 엄청난 어려움 중 하나는 이동 의미입니다. do은 움직일 때 트랜잭션을 원합니다. 그렇지 않으면 이동 된 오브젝트를 잃어 버리기 때문에 트랜잭션이 필요합니다. 그러나 항상 보장을 제공 할 수는 없습니다. std::pair<movable_nothrow, copyable>에 대해 생각해보십시오 (의견보기 참조). 이것은 당신이 noexcept 거장이되어야하고, metaprogramming의 불편한 금액을 사용해야합니다. 예외적 인 안전성 때문에 C++은 정확하게 을으로 마스터하기가 어렵습니다.

+0

'std :: pair '의 문제점은 무엇입니까? 여러분이 말했듯이, 모든 비가공 작업이 비 휴항이 아닌 작업이 던질 때 마지막으로 또는 더 나쁜 작업이 수행되도록 '쌍'을 작성하는 것은 어렵습니다. 그러나 하나의 비공개 op와 강한 예외 보장을 제공하는 경우에는 정확한 작동 순서가 존재합니다. –

+0

@SteveJessop : 예, 좋은 지적입니다. 그러나'std :: pair'의 이동 생성자는'noexcept'가 아닙니다. 또한, 이런 종류의 내 포인트 : 예외 안전이 어렵습니다, 이동 의미론 많은 템플릿 metaprogramming 푸가 필요합니다. 어떻게하면'std :: pair '의 이동 생성자를 작성하겠습니까 (인수의 역순에 유의하십시오)? 'first' 전에'second'를 만들고 싶지만,이 순서대로 선언해야합니다. –

0

예, 직면 한 문제는이 관용구가 확장하기가 어렵다는 것입니다. 다른 답변들 중 아무 것도 언급하지 않았지만 알렉산드레스쿠 (Alexandrescu)가 발명 한 또 다른 흥미로운 관용구는 scopeGuards입니다. 코드의 경제성을 높이고 강력한 예외 안전 보장을 준수해야하는 함수의 가독성을 크게 향상시킵니다.

범위 가드의 개념은 롤백 기능 개체를 각 리소스 수집에 첨부 할 수있는 스택 인스턴스입니다. 스코프 가드가 파괴되면 (예외에 의해) 롤백이 호출됩니다. 범위 종료시 롤백 호출을 피하려면 정상적인 흐름에서 commit()을 명시 적으로 호출해야합니다.

C++ 11 기능을 사용하여 안전한 스코 테 거리 설계와 관련된 this recent question from me을 확인하십시오.