Effective C++/Effective C++
[Effective C++] 32. public 상속은 "is-a"
김디트
2024. 6. 28. 09:42
728x90
항목 32. public 상속 모형은 반드시 “is-a(...는 ...의 일종이다)”를 따르도록 만들자
public 상속의 의미
- 자식 클래스의 모든 객체는 부모 클래스 객체지만, 부모 클래스 객체는 자식 클래스 객체가 아니다.
- D(Derived) is a B(Base)이지만, B는 D의 일종이 아니다.
public 상속의 예 : 새 클래스와 팽귄 클래스
- 직관적으로 만들다 보면 모호한 경우가 생긴다.
- 방식 1. 펭귄을 새의 자식으로 만들었을 때
class Bird
{
public:
virtual void fly();
...
};
class Penguin : public Bird
{
// 펭귄 역시 새지만,
// fly()를 지원하는 것이 맞는가?
...
}l
- 보완하면 다음과 같이 할 수 있을 것이다.
- 방식 2. 날 수 있는 새 관련 상속 트리를 분화시킨다.
class Bird
{
... // fly 함수를 선언하지 않는다.
};
class FlyingBird : public Bird
{
public:
virtual void fly();
...
};
class Penguin : public Bird
{
// fly 함수가 없는 부모를 상속받는다.
...
};
- 허나, 비행 능력이 있는 새와 없는 새를 구분할 필요가 없는 소프트웨어의 경우 복잡도만 늘어나는 일이다.
- 최고의 설계는 제작하려는 소프트웨어 시스템이 기대하는 바에 따라 다르며, 이상적인 설계란 없다.
- 방식 3. fly 함수를 사용할 시, 런타임 에러를 발생시킨다.
class Bird
{
virtual void fly();
...
};
class Penguin : public Bird
{
// fly 사용 시 반드시 aseertion 에러
virtual void fly() { assert(false && "Attempt to make a penguin fly!"); }
};
- 문제점
- 이 방법은 "펭귄은 날 수 없다" 는 의미를 보장해주는 것이 아니다.
- "펭귄은 날 수 있으나, 실제로 날려고 하면 에러가 난다."의 의미이다.
- 컴파일 단에서는 날 수 없음을 발견할 수 없고, 런타임 시에만 발견할 수 있다.
- 이 방법은 "펭귄은 날 수 없다" 는 의미를 보장해주는 것이 아니다.
- 인터페이스 단에서 날 수 없음을 알 수 있는 편이 좋을 것이다.
class Bird
{
...
};
class Penguin : public Bird
{
...
};
// 애초에 fly 함수를 선언하지 않는 방법
Penguin p;
p.fly(); // 에러!
좀 더 미묘한 예제
- "직사각형을 상속받는 정사각형"은 개념적으로 틀릴 게 없어 보인다.
- 하지만 실제 구현으로 가면 문제가 발생한다.
// 직사각형 클래스
class Rectangle
{
public:
int height;
int width;
};
void makeBigger_Height(Rectangle& r)
{
int oldHeight = r.height;
r.width += 10;
assert(r.height == oldHeight); // height가 반드시 변할 것
}
// 정사각형 클래스
class Square : public Rectangle
{
...
}
Square s;
...
assert(s.width == s.height); // 가로 세로가 같아야 정사각형이다.
makeBigger_Height(s); // 높이만 키우는 함수에 정사각형이 들어갈 수도 있다?
assert(s.width == s.height); // 반드시 실패한다!
- 다시 한번 반복하면,
- public 상속은 기본 클래스 객체가 가진 ‘모든 것들이’ 파생 클래스 객체에도 그대로 적용된다고 단정하는 상속
- 그러므로 이 상속 트리의 경우 틀린 것이 된다.
클래스들 사이에 맺을 수 있는 관계
728x90