이 포스트를 만든 목적

  • 미루고 미루고 미루왔던 이전 글에 대한 피드백을 위해 만들었다.
  • C++ 공부를 되돌아 보기 위해서
  • 보다 훌륭한 프로그래머가 되기 위해서

이 포스트의 준비 상황

  • C++ 문법
  • 이 포스트 전의 상황
  • 쓸만한 C++ 컴파일러
  • SyntaxHighlight
  • 쉽게 쓰려고 존댓말 생략
  • gVim 7.2

참조 링크

참조 서적

  • Exceptional C++

서론

1년 전쯤에 감자넷에서 "객체 초기화"를 소멸자로 초기화 하는 글을 읽고 댓글을 달았었다. 얼마후 댓글에 댓글이 달린것을 보게 되었고, 차이점에 대해서 코드를 남겨서 확인해야지 해야지 하다가, 이제서야 확인하게 되었다.

소멸자로 객체를 초기화 하는 코드

본론

왜 저 코드가 문제가 발생 할 수 있다고 보는가?

  1. 상속 기반 클래스에서 윗단 클래스가 소멸 될때, 밑단까지 소멸자가 호출 되는 점
  2. new 사용 시, 예외 발생으로 객체가 초기화 되지 않을 수 있는 점

추가적으로 제시 할 수 있는 문제점이 더 있는가?

  • 3. 소멸 후 생성까지 시간이 존재하므로, 멀티 쓰레드에서 초기화된 객체에 접근 가능할 수 있는 점
  • 4. 윗단 클래스의 값들은 대부분 밑단 클래스에서 사용 하므로, 설계상 문제가 생길 수 있는 점

각 문제점에 대한 코드를 줄 수 있는가?

  1. 상속 기반 클래스의 윗단 클래스가 ClearClass 를 이용하게 될 경우, 밑단 클래스까지 초기화 되고, 윗단 클래스만 생성한다는 점

    내가 지적한 문제점 1은 내가 틀렸다. 왜냐하면 T::~T() 로 인하여, 밑단 클래스 소멸자가 호출되지 않기 때문이다.
    즉, 이것은 swap() 으로 객체를 초기화 할 때와 동일한 효과를 갖는다.(윗단 클래스만 초기화 한다는 점이 같다)

  2. new 객체 생성지, 예외가 발생 되면, 해당 객체는 이미 망가졌기 때문에, 객체가 초기화 되지 않은 체 사용 될 수 있는 점

  3. 소멸자 생성 후 생성자 호출까지 시간이 존재하므로, 멀티 쓰레드에서 소멸된 객체에 접근 가능한 점
    - 예제코드는 생략한다. 멀티쓰레드 환경을 만드는 샘플은 많이 귀찮다.

  4. 윗단 클래스의 값들은 대부분 밑단 클래스에서 사용 하므로, 설계상 문제가 생길 수 있는 점
    - 예제코드 생략, 윗단의 변수들은 밑단에서 대부분 참조하므로, 윗단만 초기화 해서는 문제가 발생 될 수 있다.

제시하고자 하는 방법은 무엇이며, 어떻게 하는가?

void swap() 을 제공하여, 객체를 초기화 한다.

어떻게 하는가?

하지만 이것도 마찬가지로 객체를 초기화 하는 것에 대해서 답이 될 수 없다. 왜냐하면 1 ~ 3번 문제는 해결할 수 있으나, 4번 문제를 풀수 없기 때문이다.

결론

  • 객체를 초기화 하기 위해서, 소멸자를 이용하는 방법은 위험하며, void swap() 함수 역시 위험하다.상속 기반 클래스를 정확하게 초기화 하기 위해선, initializer 함수를 제공 할 수 밖에 없다.

여담

  • 우선 감자넷에서 제시한 코드는 무척 훌륭하다. 왜냐하면, 코드의 량을 보다 더 줄이겠다는 의지가 있기 때문이다.
  • 코드를 직접 짜보는 것과, 생각만 하는 것은 차이점이 있으므로, 실제로 코드를 짜보자.
    왜냐하면 T::~T() 가 밑단 소멸자까지 호출되지 않는다는 것을 알았기 때문이다.


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

댓글을 달아 주세요

">
  1. Favicon of http://www.Gamza.net Gamza
    2011.04.06 20:37

    1. 상속 기반 클래스의 윗단 클래스가 ClearClass 를 이용하게 될 경우, 밑단 클래스까지 초기화 되고, 윗단 클래스만 생성한다는 점
    예제가 부적절해 보입니다.
    Derived d;
    Base *pb = &d;
    ...
    ClearClass(pb) vs Base().swap(*pb);
    ClearClass와 swap 어느쪽을 사용하던 이런식의 사용은 '금지사항'입니다.
    '상위클래스만 생성'하는 것보다 '상위클래스만 초기화' 하는 것이 바람직하다고 할 수는 없을 것입니다.

    2. new 객체 생성지, 예외가 발생 되면, 해당 객체는 이미 망가졌기 때문에, 객체가 초기화 되지 않은 체 사용 될 수 있는 점
    ==>
    여러 의견들을 종합해 볼때, 예외 안정성에 있어서는 swap이 유리한것이 분명합니다.

    3. 소멸자 생성 후 생성자 호출까지 시간이 존재하므로, 멀티 쓰레드에서 소멸된 객체에 접근 가능한 점
    Base().swap(d);
    ==> swap함수 내부에서 문맥교환이 일어날 경우, 교환중인 객체의 접근이 문제가 됩니다.
    ==> swap함수가 thread safe 해야하는데, ClearClass역시 thread safe하도록 만들면 이래저래 모두 해결될 문제입니다.
    ==> swap과 ClearClass모두 같은 방식으로 해결해 두어야함'

  2. Favicon of https://www.ikpil.com 농사를 짓는 게임 프로그래머 최익필

    1번의 경우, 같은 생각입니다.

    3번의 경우, 다른 생각입니다.

    ClearClass가 전역 함수이므로, 쓰레드 세이프한 객체 또는 세이프하지 못한 객체를 쓸 것이라고 생각 됩니다. 그러므로 각 객체는 스스로가 thread safe 하다는 전제가 깔렸습니다. 각 객체마다 thread safe 하다고 했을 때, ClearClass를 이용해 Thread-safe object 를 초기화 하게 되면, 문제가 발생 할수 있습니다.

    왜냐하면 객체 내부의 mutex를 ClearClass 에서 얻어와 작업 진행 중 소멸자가 강제 호출 되므로, mutex가 망가지게 되기 때문 이기도 하며, 데드락이 발생 되기 때문입니다. 결국 ClearClass는 thread-safe 가 몹시 어렵게 됩니다.

    만약 객체의 mutex 가 외부에 있다면, 가능하겠으나, 설계상 ClearClass를 thread-safe, non thread-safe 로 두개 만들고, 구분해서 사용해야 됩니다. 이것은 "만약" 이 더 생기게 되므로, ClearClass 의 목적인 "간결함"을 해칩니다.

    그러므로 thread-safe 에 대응은 전역 ClearClass보다 멤버 swap이 더 좋습니다.

    감사합니다.