갑작 스러운 질문 부터 시작한다.

  • 자원을 단순히 관리하고 마지막 try/catch 구문을 덤으로 제거할 수 있는 좀 더 진보적인 기술들을 어떻게 적용할 수 있을까?
  • 내재된 데이터형 T의 요구사항을 줄여서 Stack을 개선할 수 있는 방법이 있을까?
  • 일반적인 컨테이너에서 예외 설계를 사용할 수 있을까?
  • new[]와 delete[]가 정말로 하는 일은 무엇일까?

지금까지 만들어본 Stack 의 주요 예외안전성을 메모리를 관리하는 방법에 그 초점을 두고 있다. 따라서, 이런것만 따로 관리하는 보조클래스를 한쪽에 두는것이 좋을 듯 싶다.

다음 코드를 이런 초점에 맞추어서 만들어진 클래스이다.


자.. 이제 문제의 시간이 돌아왔다.

1. StackImpl의 세개의 멤버 함수 모두를 예전 방식과는 다르게 구현해 보자. 어느 상황에서든지 v_ 버퍼의 컨테이너의 T 개체의 수만큼 많지도 적지도 않게 완전히 같은 T 개체를 담고 있음을 가정하자.

2. StackImpl의 의무사항을 기술해 보자.

3. /* ??? */에 뭐가 들어가야 할까? StackImpl이 사용하게 될 방법에 선택이 미치는 영향이 무엇인지 가능한 구체적으로 말해라.

 

 

해결책

1.
생성자

보면 알다 시피, StackImpl 생성자에서 메모리 할당을 한다. 이해하는데 별 무리는 없지만 바로 3라인의 operator new 가 생소해 보일 수도 있다. operator new( size_t ) 를 넣게 되면, T의 생성자 호출 없이 이 메모리 공간만 할당 되어진다.(지금은 이 정도만 알고 넘어가도 된다. 주의해야 할것은 이렇게 생성된 T 타입을 해제하려 할때 명시적으로 T::~T()를 호출해 주어야 한다는 것이다.(나중에 설명할 일이 있을듯) )

이전 항목들에서 배웠듯이 만약 operator new(size_t) 를 호출하는 중 예외가 발생하면 StackImpl 생성자는 온전히 만들어지지 못했으므로 아무것도 아닌게 되고 예외는 StackImpl 생성자의 호출자에게 전가된다. 이것은 예외에 중립적인것을 뜻한다.

소멸자

소멸자는 알다 시피, 예외를 던지지 말아야 한다. operator delete( v_ ) 를 하면 첫번째 메모리만 날라가게 되는데, 이것은 operator new 할때, 메모리가 실질적으로 어디에 생성되는지 안다면 이렇게 해도 된다는 것을 알것이다. 단지 T 타입의 소멸자를 호출해 주는 로직이 보이지 않는데

그것은 destory 함수에서 v_ 에서 v_+vused_ 까지 루프를 돌면서 알아서 소멸자를 호출해 줄것이다. 물론 T::~T() 소멸자에서도 예외를 던저선 안된다.

Swap

머리속으로 이 코드를 생각해 본다면, 자신이 갖고 있는 모든 것들을 교환 시킨다.(swap 함수는 기본적으로 알것이라고 생각한다). 또한 throw() 로 그 어떤 예외도 던지지 않는 완벽한 보증을 하게 된다. swap 은 단순한 값 교체이므로 예외를 던지지 않는다.

 

각각의 함수들을 정리해보면, 한가지 규칙에 따른다. "항상 코드의 각 조각에 하나의 잘 정의된 책임감을 부여하자" 이것은 예외 보장에 승리하기 위한 초석이 된다.

그럼 /* ???? */ 으로 주석 처리 된 곳에 public: 이 들어가야 할까? protected 또는 private 이여야 할까?
이 부분은 책의 내용을 보았을 때, 잘 이해가 가지 않는다. - 다시 봐야할 부분 -

 

총평
각각의 역활을 분배하는것은 예외 보장을 더욱 견고하게 해주는 것을 배운다. 메모리 관리 부분 StackImpl에서 관리하고 각 데이터 관리 부분은 Stack 이 관리 함으로써, 서로 다른곳의 예외에 대해서만 보장을 한다면, 전체적으로 예외에 보장을 할 수 있다는 것을 배웠다.

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기