항목 34 인터페이스 상속과 구현 상속의 차이를 파악하고 구분하자
34.1 인터페이스 상속과 구현 상속
- 멤버 함수 인터페이스 상속
- 함수 인터페이스 상속은 함수 선언을 물려주는 것과 같다.
- 부모 클래스의 멤버 함수 인터페이스는 is-a 관계에 의하여 항상 자식 클래스에게 물려준다.
- 멤버 함수 구현 상속
- 함수 구현 상속은 함수 정의를 물려주는 것과 같다.
- 부모 클래스의 멤버 함수 구현은 멤버 함수의 세 가지 유형에 따라서 다르게 물려준다.
상속 | 순수 가상 함수 | 가상 함수 | 비가상 함수 |
인터페이스 상속 | O | O | O |
구현 상속 | X | △ | O |
■ 34.1.1 순수 가상 함수
- 순수 가상 함수(pure virtual function)는 자식 클래스에게 함수의 인터페이스만 물려준다.
- 일반적으로 부모 클래스에 있는 순수 가상 함수는 정의하지 않지만, 정의할 수는 있다.
- 부모 클래스에 있는 순수 가상 함수는 자식 클래스에서 재정의(override)해야 한다.
■ 34.1.2 가상 함수
- 가상 함수(virtual function)는 자식 클래스에게 함수 인터페이스와 기본적인 함수 구현을 물려준다.
- 부모 클래스에 있는 가상 함수는 자식 클래스에서 재정의(override)할 수 있다.
- 자식 클래스에서 가상 함수를 물려받고 싶지 않은 경우
- 첫 번째, 가상 함수의 인터페이스(public 순수 가상 함수)와 구현(protected 비가상 함수)을 분리한다.
- 두 번째, 순수 가상 함수의 인터페이스를 정의한다.
// 첫 번째, 순수 가상 함수로 인터페이스를, 비가상 함수로 구현을 분리하여 호출한다.
class Airport { };
class Airplane {
public:
virtual void Fly(const Airport& destination) = 0;
protected:
void DefaultFly(const Airport& destination) { }
};
// 모델 A는 부모 클래스의 DefaultFly 비가상 함수를 호출합니다.
class ModelA : public Airplane {
public:
virtual void Fly(const Airport& destination) override;
};
void ModelA::Fly(const Airport& destination) {
DefaultFly(destination);
}
// 모델 B는 부모 클래스의 DefaultFly 비가상 함수를 호출하지 않습니다.
class ModelB : public Airplane {
public:
virtual void Fly(const Airport& destination) override;
};
void ModelB::Fly(const Airport& destination) { }
int main(void) {
Airport airport;
Airplane* airplane = new ModelB;
airplane->Fly(airport);
return 0;
}
// 두 번째, 순수 가상 함수의 인터페이스를 정의한다.
class Airport { };
class Airplane {
public:
virtual void Fly(const Airport& destination) = 0;
};
void Airplane::Fly(const Airport& destination) { }
// 모델 A는 부모 클래스의 Fly 순수 가상 함수를 호출합니다.
class ModelA : public Airplane {
public:
virtual void Fly(const Airport& destination) override;
};
void ModelA::Fly(const Airport& destination) {
Airplane::Fly(destination);
}
// 모델 B는 부모 클래스의 Fly 순수 가상 함수를 호출하지 않습니다.
class ModelB : public Airplane {
public:
virtual void Fly(const Airport& destination) override;
};
void ModelB::Fly(const Airport& destination) { }
int main(void) {
Airport airport;
Airplane* airplane = new ModelB;
airplane->Fly(airport);
return 0;
}
■ 34.1.3 비가상 함수
- 비가상 함수(non-virtual function)는 자식 클래스에게 함수 인터페이스와 필수적인 함수 구현을 물려준다.
- 비가상 함수는 자식 클래스에서 재정의할 수 없다.
- 비가상 함수는 상속과 상관없이 변하지 않는 동작, 즉 불변 동작을 물려받는다.
34.2 클래스 설계 실수
■ 34.2.1 모든 멤버 함수를 비가상 함수로 선언하기
- 부모 클래스로 활용할 클래스는 가상 함수를 갖는 것이 바람직하다.
- 가상 함수는 비가상 함수보다 비용이 더 들어가나, 프로그램 전체 성능에 영향을 줄 만큼 문제가 되지 않는다.
- 80-20 법칙에 따라서 프로그램 전체 성능에 영향을 주는 20% 코드에 더 집중해야 한다.
■ 34.2.2 모든 멤버 함수를 가상 함수로 선언하기
- 불변 동작이 필요하다면 부모 클래스는 비가상 함수를 갖는 것이 바람직하다.
NOTE
① public 상속에서 자식 클래스는 부모 클래스의 인터페이스를 항상 물려받는다.
② 순수 가상 함수는 인터페이스만 상속한다.
③ 가상 함수는 인터페이스 상속과 기본적인 구현을 상속한다.
④ 비가상 함수는 인터페이스 상속과 필수적인 구현을 상속한다.
'Object Oriented Programming(C++) > Effective C++' 카테고리의 다른 글
Effective C++ | 항목 37 어떤 함수에 대해서도 상속받은 디폴트 인수는 절대로 재정의하지 말자 (0) | 2024.07.26 |
---|---|
Effective C++ | 항목 36 상속받은 비가상 함수를 자식 클래스에서 재정의하는 것은 절대 금물! (0) | 2023.05.21 |
Effective C++ | 항목 33 상속된 이름을 숨기는 일은 피하자 (0) | 2023.05.20 |
Effective C++ | 항목 32 public 상속 모형은 반드시 is-a를 따르도록 만들자 (0) | 2023.05.18 |
Effective C++ | 항목 27 캐스팅은 절약, 또 절약! 잊지 말자 (0) | 2023.03.06 |
댓글