이번 항목은 가상함수를 사용시, 주의해야 할 점을 설명하고 있다.
다음 코드를 보자.

질문의 시간이다. 이 main 함수부터 시작 된 이 코드는 프로그래머가 무엇을 의도 했을 것 같은가?


분석

우선 문제가 있는 코드 부분부터 알아보고, 가상함수를 뜯어 보자.

문제가 있는 코드

 이 코드를 보면 main 의 반환타입이 void 이다. 이것은 잘못 된 것이다. 구글에서 검색해 보아도, 확실히 알 수 있을 것이다. 이것은 표준이 아니며, 다른 컴파일러에선 안될 수도 있다. 잘 정리된 외국 싸이트 하나를 링크 건다. 구글에서 제일 위에 있는 글이다. http://users.aber.ac.uk/auj/voidmain.shtml

이 코드의 문제점은 pb 가 Base* 이지만, 실제 객체는 Derived 임에도 불과하고, Base* 형으로 delete를 하면서 소멸자를 가상함수로 만들지 않았다는 데에 있다.

일반적으로 기반 클래스의 소멸자를 가상 소멸자로 만들어야 한다고 많은 책들이 말하지만, 이건 잘못 된 것이다. 곰곰히 생각해 보면, 기반 클래스형으로 파생 객체를 컨트롤하지 않는다면, 굳이 쓸 필요는 없다. 물론 클래스 상속이 그런 이유[각주:1]에서 하는 것이지만 말이다.^^


이제 본론으로 들어가서 이 프로그래머의 의도와 사용 상태를 비교하여 풀이해보자. 다음 코드를 보자.

 여기까지는 문제가 없다.

 이 코드의 의도는 분석 할 수가 없다. 왜냐하면

  1. Derived::f 는 Base::f 를 모두 가린다. 이것을 무엇을 위해 가려야 했는지 분석 할 수가 없다.
  2. 암울하게도 Complex가 암시적 형변환을 제공하기 때문에, 컴파일이 되고, void Derived::f( complex<double> )가 호출 되어 진다.


 이 코드는 Base::f( double ) 가상 함수를 호출하여 Derived::f 호출을 기대 한것으로 보인다. 하지만 이것은 Base::f( double ) 이 호출 되어 진다. 왜냐하면 이 프로그래머는 중대한 실수를 했다.

  • 함수 오버라이드 사용 시, 기반클래스의 가상 함수 인터페이스를 그대로 파생 클래스에서 사용 해야 한다.

 이 코드에서의 pb->g() 에서 20을 기대 했을 것이지만 그렇지 못하다. 자세한 이유에 대해서는 Effective C++ 3판에 나온다. 한마디로 요약하자면,

  • Base::g() 호출 하고, 가상 테이블에 하위 호출이 있다면, Derived::g() 를 호출하기 때문에, Base::g() 호출 단계에서 이미 10값이 들어 갔기 때문이다.


자, 그렇다면 이번 항목에서 배운 모든 것들을 총 정리해 보자.

  1. 함수 오버로딩은 같은 범위 내에 같은 이름을 가지며, 같은 매개변수 타입과 같은 반환 타입을 갖어야 한다.
  2. 가상 함수가 디폴트 매개변수를 지원한다면, 기반 클래스의 기본 매개변수의 영향을 받는다.
  3. 기반 클래스의 참조형으로 객체를 파괴 할 경우, 기반 클래스의 소멸자는 가상 소멸자여야만 한다.


총평

음.. Effective C++ 3판을 다시 정리한 느낌. 짦지만 굵은 이야기들이였다.




  1. 대부분의 코드들에서 public 상속을 코드를 재사용하기 위해서 사용 하지만, 이는 잘못된 행위라고 생각한다.. 개념자체가 상속 되어야만 할때, public 상속을 써야 한다고 나는 보고 있다. 여기에 대한 이야기는 다음 항목에서 설명 한다. [본문으로]
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기