본문 바로가기

Effective C++/6. 상속, 그리고 객체 지향 설계

항목 32: public 상속 모형은 반드시 "is-a(…는 ...의 일종이다)"를 따르도록 만들자

public 상속은 "is-a(… ... 일종이다.)" 의미한다.

 

말은 기억하도록 하자.


만약 Derived 클래스를 Base 클래스로 부터 public 상속을 통해 파생시켰다면, 이것은 컴파일러에게 다음과 같이 말한 것이다.

 

"Derived 타입으로 만들어진 모든 객체는 또한 Base 타입의 객체이지만, 반대는 되지 않는다"

 

Base Derived 보다 일반적인 개념을 나타내며, Derived Base보다 특수한 개념을 나타낸다.

밴다이어그램으로 표현하면 다음과 같은 의미일 것이다.

 

 

C++ public 상속을 이렇게 해석하도록 문법적으로 지원하고 있다.

 

 

  예제는 public 상속에서만 통한다. 


public 상속 = is-a 관계  <= 이야기는 직관적이고 간단하지만, 이런 직관성때문에 잘못 판단하는 경우가 있다. 예를 들어, 펭귄이 새의 일종이라는 사실은 누구나 알고, 새는 있다는 점도 사실이다. 이것을 C++ 표현하면 다음과 같은 코드가 나올 것인데

 

 

 

뭔가 이상함이 느껴진다. 위의 클래스 계통에 의하면 펭귄은 있다.

=> 자연어에 낚인 케이스이다. "새는 있다"라는 말이 모든 종류의 새에 대해 참인 명제가 아니기 때문이다. 따라서 조금 현실적으로 바꿔보자면

 

 

있는 새를 따로 분류하는 것이 조금 현실에 충실한 설계가 아닐까 싶다. 하지만 이는 현실에서의 이야기이고, 만약 어떤 소프트웨어를 설계하는데 비행관련 작업을 일이 없으면 이런 분류는 필요 없을 것이다. 우리가 본뜨려고 하는 세계가 어떤 것이냐에 따라 고려해도 되고 안해도 그만인 것이다.

 

혹은 이런 방식으로 해결은 안될까?

펭귄이 상속받은 fly() 재정의하여 에러가 나게 하는 것이다.

 

 

경우는 "펭귄이 없다" 아니라 "펭귄은 있다. 그러나 펭귄이 실제로 날려고 하면 에러가 난다" 이다.

실제로 날려고 하면 => 프로그램 실행때만 에러를 있다.

 

방법보단 그냥 fly() 빼는 것이 좋다. 왜냐하면 펭귄이 fly() 호출하면 컴파일 에러가 것이기 때문이다.

 

유효하지 않은 코드를 컴파일 단계에서 막아주는 인터페이스가 좋은 인터페이스이다.


예제를 하나 더보면, Square(정사각형) 클래스는 Rectangle(직사각형) 클래스로부터 상속받아야 한다고 생각할 있다. 수학적 정의로 보면 맞긴한데, 아래 코드를 보자.

 

 

example() 단정문은 정사각형이면 실패해선 안된다. 당연히 정사각형의 가로 세로 길이는 같아야 하기 때문이다. 그런데 뭔가 이상함이 느껴진다.

 

  • makeBigger() 호출 , s 가로 세로 길이는 같아야 한다.
  • makeBigger() 호출 , s 가로 길이는 변하는데, 세로 길이는 변하면 안된다.
  • makeBigger() 호출 , s 가로 세로 길이는 같아야 한다.

 

=> 말이 안된다. 아런 문제가 발생했느냐? => 직사각형의 어떤 성질 정사각형에는 적용이 안되는 성질이 있는 것이다. 직사각형은 가로 세로 길이가 달라도 되지만, 정사각형은 같아야 한다!

 

public 상속은 기본 클래스 객체가 가진 모든 것들 파생 클래스 객체에도 그대로 적용된다고 단정짓는 상속이다.

 


  • public 상속의 의미는 "is-a(…... 일종)" 이다. 기본 클래스에 적용되는 모든 것들이 파생 클래스에 그대로 적용되어야 한다. 왜냐하면 모든 파생 클래스 객체는 기본 클래스 객체의 일종이기 때문이다.