저자의 생각이 반영된 글이므로

틀린 내용이나 다른 접근을 말씀해주시면
적극 검토해 보겠습니다.



What is Exception and why is Exception-safety guarantee needed?

--------------------------------------------------------------------------------------------

  Exception이란 프로그래머가 디자인한 흐름대로 실행이 되지 않아 더이상 정상적인 진행이 불가능한 경우를 말한다. 예를들어 성적을 저장할 장소를 마련하기 위해 메모리를 할당해야 하는데 공간이 충분하지 않아 할당을 못한경우 그대로 성적을 저장할 수 없으므로 할당이 안된 경우도 하나의 Exception이다. 간단히 에러라고 생각해도 무방할듯 하다.

  Exception을 두는 이유가 무엇일까? 그 이유는 곰곰히 생각해보면 의외로 간단하다. 이해를 돕기 위해 윈도우를 잘 사용하다가 시스템 영역의 무언가를 잘못 건들여서 부팅이 안된다고 가정해보자(부팅오류Exception이 발생했다고 생각해보자). 억지로 윈도우를 돌아가게 만들어도 그것이 앞으로도 동작할지는 아무도 보장을 못할 뿐더러(불안한 PC에서 중요한 작업을 하고 싶은가?) 이런 경우에는 계속 진행하게 하고 싶어도 방법이 없다. 이경우 건들기 전의 상태로 돌아가게 하거나, 완전히 지우고 윈도우를 재설치 하는 방법을 생각해 볼 수 있는데 바로 이것이 우리가 Exception을 사용하는 궁극적인 이유이다. 즉 프로그램은 내부에서 Exception이 발생한 경우 Exception이 발생되기 전의 상태로 돌아가 다시 정상적인 프로세스를 진행할 수 있게 하는데 그 의의가 있다.





What is Exception-safe guarantee?
--------------------------------------------------------------------------------------------
  따라서 우리가 이 클래스(또는 함수 등등..)는 예외 안전성이 보장되어 있습니다 라고 말하는 것은 만약 이 함수를 쓰다가 Exception이 발생한 경우 그에 따른 대처가 포함되어 있다는 것을 말하고 있는 것이다. 어떤 함수가 예외 안전성을 보장한다고 하면 아래의 두 항목이 보장되어야 한다.
  • 자원 낭비가 없어야 한다 (즉 Memory Leak 이 없어야 한다)
  • Exception이 발생하기 전 상태의 값들을 보장해야 한다.
두번째 항목에 좀 더 부연 설명을 하자면, 만약 내가 A 라는 변수의 메모리를 해지하고 나서 B의 메모리를 할당하는 순간 Exception이 발생하게 되면, 해제되어 있던 A변수는 메모리가 잡혀있던 이전 상태로 되돌려야 한다는 말이다. 이는 모든 변수에 해당하는것이 아니며, 프로세스를 돌릴때 필요한 것들만 복구가 되면 된다.

  이런 예외 안전성 보장은 크게 3종류로 볼수 있다.
  • Basic Guarantee (기본적인 보장) : 예외발생시 모든것들을 유효한 상태로 유지함을 보장한다. 유효한 상태로만 되돌리므로 프로그램은 그 이후로 진행이 가능하나 단지 임의의 유효한 상태로 바꾸어 둔 것이기 때문에 앞으로 어떻게 될지 예측할 수 없다.(예측하려면 현재의 값들을 읽고 판단하는 루틴을 따로 만들어야 겠다. 하나의 오버헤드가 될 수 있는 것)
  • Strong Guarantee (강력한 보장) : 예외발생시 프로그램 상태를 절대 변경하지 않는다는 것을 보장한다. 즉 예외가 발생하지 않으면 정상동작을 하는 것이고, 예외가 발생하면 호출이 없었던 것처럼 상태로 되돌아감을 의미.
  • Nothrow Guarantee (예외불가 보장) : 예외를 절대 발생시키지 않음을 보장한다. 즉 호출되면 무조건 동작하게 되어있음을 의미한다. 기본자료형(int등..)은 절대 예외를 발생시키지 않는다. 또한 특정 클래스에 맞게 customize한 swap이 이러한 예외불가를 보장한다면, 특정클래스의 exception발생시 이전값으로 되돌리는데 이 함수를 이용할 수 있는 이점이 있다.



How can we guarantee Exception-safe?
--------------------------------------------------------------------------------------------
  예외를 보장하려면 어떻게 해야 할까? 질문을 달리 해보자. 만약 Exception이 발생한경우 어떻게 이전 값으로 되돌릴 것인가? Effective C++에서 추천하는 방법중에 하나는 copy-and-swap 이다. 원래 오브젝트의 복사본으로 작업하다가 완료되는 시점에 값을 맞바꾼다는 전략이다.

  예를 들어보자. 만약 test.bmp 파일을 myObject안에 집어넣는 함수를 이용한다고 가정했을때 copy-and-swap을 적용한 경우 어떤 이점이 있는지 살펴보자. 우선 적용 전의 코드를 보자.

int function1 (MyClass& myObject)
{
   myObject.image.clear();
   myObject.image = new Image("test.bmp");
}

이 경우 new 연산에서 할당공간 부족으로 메모리를 할당하지 못한 경우 이전으로 되돌릴 수 없다. 왜냐하면 이미 clear연산을 통해 내부가 지워졌기 때문이다. 다음의 코드를 보자

int function1 (MyClass& myObject)
{
   std::tr1::shared_ptr<MyClass> myObjectCopy(new MyClass);  // Copy
   myObjectCopy->image = new Image("test.bmp");
   swap( myObject, *(myObjectCopy) );                                    // Swap
}

아직 exception 발생시 복구하는 루틴은 넣지 않았지만 만약 new에서 Exception이 발생할때 리턴을 한다면 수정 전 상태로 되돌아 갈 수 있다. 독자들 중에는 과연 이 방법이 좋은것인가에 의문을 품는 분들도 계실것이다. 그렇다. swap으로 두 객체를 맞바꾸게 되는부분에서 객체의 데이터 사이즈가 어마어마하게 큰 경우는 swap 자체로 굉장한 overhead가 될 수 있기 때문이다. 이때문에 프로그램의 성격이 중요하다. 느리더라도 안전하게 돌아가야 하는 것은 이와 같은 방법을 고수하여 사용해야 할 것이고, 그다지 안전성을 보장하지 않고 속도가 빨라야 한다는 것은 다른 방법을 쓰거나 Exception-safe를 보장하지 않으면 되는 것이다.



Conclusion
--------------------------------------------------------------------------------------------

 이왕이면 강력한 예외 안전성 보장인 예외불가를 보장하는 것이 바람직하나, 현실적으로 힘든 경우가 많이 있다. 앞의 예제에서 보았듯이 강력한 보장조차도 설계시 효율성이나 기타 다른 요인에 의해 할 수 없는 경우가 비일비재하다. 따라서... 프로그램의 성격을 잘 생각하고, 잘 설계하자. by Leanu


Introduction
----------------------------------------------------------------------------------------------
  기존의 swap은 어떤 타입이든(대입 연산자 '=' 를 이용할 수 있다면) 가능한 std 범용 함수이다. 하지만 클래스 내부 멤버 변수가 엄청나게 큰 데이터를 가지고 있다면, 복사에 드는 비용은 실로 어마어마하다고 할 수 있다. 이런경우 두 객체의 멤버 변수 포인트만 바꿔주는, 기존의 동작과는 약간 다른 특화된 swap을 만들 필요가 있다. 지금부터 설명하고자 하는 특화된 swap에 대한 방법은 Effective c++에서 소개한 방법을 인용하기로 한다.

Before implementing specialized swap function
----------------------------------------------------------------------------------------------

    1. 표준 제공 swap함수의 성능이 내가 만든 클래스에서 만족할 만한 성능을 지니면
       그냥 쓴다.

    2. 그렇지 않은 경우 클래스 내부에 swap 멤버 함수를 퍼블릭으로 선언하라. 그리고
       그 안에는 빠른 swap 알고리즘을 기술한다.

       (예) MyClass {
            public:
                ...
                swap(MyClass target) { // Write Fast swap algo }
            private:
                ...
           };

    3. 내가 구현한 클래스를 둘러싸고 있는 네임스페이스 안에 swap 함수를 많들어 넣고
       이 함수 안에서 MyClass에서 만든 swap 멤버 함수를 호출하도록 만든다.

        namespace MyNamespace
        {
             ...
             MyClass {
                 ...
             };
             ...
             void swap(MyClass& a, MyClass& b)
             {
                  a.swap(b);
             }
        } // end - MyNamespace
      
  < Suggestion > swap은 절대로 예외를 발생시키자 않도록 만들어라. swap은 예외 발생시 이전상태로 돌릴수 있게 응용할 수 있는 강력한 함수이기 때문에(strong- exception-safety guarantee) 예외를 던지지 않게 설계한다면 유용하게 이용할 수 있을 것이다.

잼나네 ㅎㅎ








동영상을 더 보고 싶다면...
클릭

+ Recent posts

티스토리 툴바