728x90
반응형

결론부터 얘기하면, 상속받은 비가상 함수를 파생 클래스에서 재정의 하지 말자는 것이다.

상속받은 비가상 함수를 파생 클래스에서 재정의한 아래의 예시를 보자.

class B
{
public:
	void mf();
	...
};

class D: public B
{
public:
	void mf();
	... 
};

---

D x;
B *pB = &x;
pB->mf(); // B::mf()를 호출

D *pD = &x;
pD->mf(); // D::mf()를 호출

이렇게 두 가지 동작을 하게 되는 이유는 B::mf 및 D::mf 등의 비가상 함수는 정적 바인딩(static binding)으로 묶이기 때문이다.

즉, pB는 'B에 대한 포인터' 타입으로 선언되었기 때문에 pB를 통해 호출되는 비가상 함수는 항상 B 클래스에 정의되어 있을 것이라고 결정한다는 말이다. 이건 B에서 파생된 객체를 pB가 가리키고 있다고 해도 위처럼 마찬가지의 동작을 한다.

반면, 가상 함수의 경우엔 동적 바인딩(dynamically binding)으로 묶인다. 만약 mf 함수가 가상 함수였다면 mf가 pB에서 호출되나 pD에서 호출되나 D::mf가 호출된다. pB 및 pD가 진짜로 가리키는 대상은 D타입의 객체이기 때문이다.

 

항목 32에서 public 상속의 의미는 "is-a(.. 는.. 의 일종이다)"이다. 그리고 항목 34에서 비가상 멤버 함수는 클래스 파생에 관계없는 불변 동작을 정해두는 거라고 이야기했었다. 이 두 가지 포인트를 B, D 클래스 및 비가상 멤버 함수인 B::mf에 그대로 가져가면 이렇게 풀 수 있다.

  • B 객체에 해당되는 모든 것들이 D 객체에 그대로 적용된다. 왜냐면 모든 D 객체는 B 객체의 일종이기 때문이다.
  • B에서 파생된 클래스는 mf 함수의 인터페이스와 구현을 모두 물려받게 된다. mf는 B 클래스에서 비가상 멤버 함수이기 때문이다.

여기서 D에서 mf를 재정의하는 순간 설계에 모순이 생긴다.

결과적으로 어떤 상황에서도 상속받은 비가상 함수를 재정의 하는 것은 절대 금물이다.

 

요약

  • 상속받은 비가상 함수를 재정의하는 일은 절대로 하지 말자.

 

728x90
반응형