본문 바로가기
Object Oriented Programming(C++)/Effective C++

Effective C++ | 항목 36 상속받은 비가상 함수를 자식 클래스에서 재정의하는 것은 절대 금물!

by continue96 2023. 5. 21.

Effective C++ 항목 36

 

항목 36 상속받은 비가상 함수를 자식 클래스에서 재정의하지 말자

36.1 재정의한 비가상 함수

  • 비가상 함수정적 바인딩(static binding)으로 묶인다.
    • pParent는 Parent에 대한 포인터 타입으로 선언되었기 때문에 pParent를 통해 호출되는 비가상 함수는 항상 Parent 클래스에 정의되어 있다고 결정한다.
  • 가상 함수동적 바인딩(dynamic binding)으로 묶인다.
    • mf 함수가 가상 함수였다면 pParent와 pChild가 가리키는 대상은 Child 타입의 객체이므로 무조건 Child::mf()가 호출된다.
  • 비가상 함수를 재정의하면 객체를 가리키는 포인터나 참조자 타입에 따라 일관성 없는 동작을 보이므로 상속받은 비가상 함수를 재정의하면 안 된다.
class Parent {
public:
	void mf() {
		cout << "Parent의 mf 함수를 호출합니다." << endl;
	}
};

class Child : public Parent {
public:
	void mf() { /* 비가상 함수를 재정의하여 Parent::mf()를 가립니다. */
		cout << "Child의 mf 함수를 호출합니다." << endl;
	}
};

Child child;
Parent* pParent = &child; /* child 객체를 Parent 포인터로 가리킵니다. */
Child* pChild = &child; /* child 객체를 Child 포인터로 가리킵니다. */

pParent->mf(); /* Parent의 mf 함수를 호출합니다. */
pChild->mf(); /* Child의 mf 함수를 호출합니다. */

 

36.2 재정의한 비가상 함수의 잘못된 의미

  •  public 상속의 의미는 is-a이고, 비가상 멤버 함수는 클래스 상속과 관계없는 불변 동작을 정의한다.
    • 자식 객체는 부모 객체의 일종이기 때문에 Parent 객체에 해당하는 모든 것은 Child 객체에 그대로 적용된다.
    • mf()는 부모 클래스에서 비가상 멤버 함수이기 때문에 자식 클래스는 mf()의 인터페이스와 구현을 모두 물려받는다.
  • 이때, 자식 클래스에서 mf()를 재정의하면 위의 설계에 모순이 생긴다.
    • 자식 클래스에 mf()를 재정의하는 경우, 자식 객체는 부모 객체의 일종이라는 명제가 거짓이 되고
    • mf()는 클래스 상속과 상관없이 부모 클래스에 대한 불변 동작을 나타낸다는 명제가 거짓이 된다.

 

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

댓글