항목 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
① 상속받은 비가상 함수를 절대로 재정의하지 말자.
'Object Oriented Programming(C++) > Effective C++' 카테고리의 다른 글
Effective C++ | 항목 38 has-a 혹은 is-implemented-in-terms-of를 모형화할 때는 객체 합성을 사용하자 (0) | 2024.08.06 |
---|---|
Effective C++ | 항목 37 어떤 함수에 대해서도 상속받은 디폴트 인수는 절대로 재정의하지 말자 (0) | 2024.07.26 |
Effective C++ | 항목 34 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구분하자 (0) | 2023.05.21 |
Effective C++ | 항목 33 상속된 이름을 숨기는 일은 피하자 (0) | 2023.05.20 |
Effective C++ | 항목 32 public 상속 모형은 반드시 is-a를 따르도록 만들자 (0) | 2023.05.18 |
댓글