Post List

2015년 1월 4일 일요일

다형성을 가진 Class는 소멸자를 반드시 virtual로 선언하자.

* 다형성 (Polymorphism)을 가진 Class는 반드시 virtual 소멸자를 선언해야 합니다. 즉, 어떤 Class가 virtual 함수를 하나라도 갖고 있으면, 소멸자도 virtual 소멸자여야 합니다.


class Clock {
public:
    Clock();
    ~Colock();
    Clock* getClock();  // Factory 함수 : 파생된 Class 에서 기본 Class의 Pointer를 반환
    ...
};

class AtomicClock : public Clock { ... };
class WaterClock : public Clock { ... };
class WristWatch : public Clock { ... };

AtomicClock ac;
Clock *clock = ac.getClock(); // 파생된 Class에서 기본 Class Pointer를 활용
...
delete clock;                 // 기본 Class의 Pointer로 삭제 ?



 위의 예제를 보면 Clock Class의 소멸자가 비가상 소멸자(non-virtual destructor)다. 이는 실로 어처구니없는 재앙을 12개월 할부로 앞당겨 구매하는 방법이다. C++ 규정에 의하면 기본 Class를 통한 파생 Class의 객체가 삭제될 때 기본 Class의 소멸자가 비가상 소멸자일 경우 기본 Class 부분은 소멸되지만, 파생 Class 부분은 소멸되지 않는다. 이것을 부분 소멸(Partially destroyed) 이라고 한다. 소중한 자원이 오뉴월의 송아지 오줌처럼 시원하게 새어나갈 것이다.

 이 문제를 해결하는 방법은 ? 간단하다. 사랑하는 기본 Class에 가상 소멸자를 선물하면 끝 !


class Clock {
public:
    Clock();
    virtual ~Colock();
    ...
};


* 상속되도록 설계되지 않았거나 다형성을 갖도록 설계되지 않은 Class에는 virtual 소멸자를 선언하지 말아야 합니다.


class Point {
public:
    int x,y;
    ...
};



 위의 Class의 경우 int가 32bit라고 가정한다면 정확히 64bit 레지스터에 딱 맞는 크기가 된다. 그래서 다른 언어로 작성된 함수에 넘길 경우에도 정확히 64bit 크기로 넘어간다. 하지만 위의 Class에 가상 소멸자를 넣으면 어떻게 될까 ? 가상 함수를 가진 Class는 vptr (virtual table pointer) 을 가지게 된다. 즉 64bit 였던 원래 크기가 32bit 시스템의 경우 96bit, 64bit 시스템의 경우 128bit로 크기가 늘어날 뿐더러 다른 언어로의 호환성은 상실하게 된다.

 즉, 가상 함수가 하나라도 있는 경우에만 가상 소멸자를 선언해 주는 것이 맞다.

 참고로 std::string, 및 STL의 컨테이너들 (vector, list, set, tr1::unordered_map) 등은 가상 소멸자가 없는 Class이다. 이런 Class를 상속받아서 사용하는 일은 자제해주길 바란다. Java에는 final class, C#에는 sealed class 등의 파생 클레스 방지 매커니즘이 있지만, C++에는 없다.

댓글 없음:

댓글 쓰기