스토리텔링 개발자

[Effective C++] 32. public 상속은 "is-a" 본문

개발/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 상속은 기본 클래스 객체가 가진 ‘모든 것들이’ 파생 클래스 객체에도 그대로 적용된다고 단정하는 상속
  • 그러므로 이 상속 트리의 경우 틀린 것이 된다.

 

 

 

클래스들 사이에 맺을 수 있는 관계
  1. is-a(...는 ...의 일종이다.) (public 상속)
  2. has-a(...는 ...를 가진다.) (항목 38, 항목 39 참조)
  3. is-implemented-in-terms-of(...는 ...를 써서 구현한다) (항목 38, 항목 39 참조)
728x90
Comments