public 상속은 반드시 is-a 관계를 따르도록 하자.


사람 <------ 학생

위의 표현은 C++ 기본서에서

public 상속을 설명할 때 가장

빈번히 활용되는 예이다.




학생은 사람이라서 사람이 할 수 있는

(먹기, 앉기, 눕기 등등)을 할 수 있지만

모든 사람은 학생이 아니기 때문에

모든 사람은 학교를 다닌다가 참이 될 수 없다.




즉, Base클래스 (사람) 은 일반적인 개념을 타나내며

Drived클래스 (학생) 은 좀 더 특수화된 개념을 나타낸다.


public 상속과 is-a 관계가 똑같은 뜻이라는 이야기는

꽤 직관적이고 간단하긴 하지만, 그 직관 때문에 잘못된

판단을 하는 경우가 있다. 아래의 예제를 보도록 하자.

펭귄은 새의 일종이고, 새는 날 수있다는 것을 알고있다.




class Bird{

public:

virtual void fly();            // 새는 날 수 있다.

...

};

class Penguin: public Bird{    // 펭귄은 새이다.

...

};


새는 날 수 있다는 사실을 바탕으로 클래스를 디자인 했는데

펭귄이라는 새는 날지 못한다. 모든 새는 날지 못한다는 점도 

구분하여 좀 더 현실에 가까운 클래스 계통구조를 만들어야 한다.




class Bird {

...                                            // fly 함수가 없다.

}


class FlyingBird : public Bird{

public:

virtual void fly();

...

};


class Penguin: public Bird {

...                                            // fly 함수가 없다.

};




좀 더 현실에 가까운 클래스 구조가 되었다.

하지만 어떠한 소프트웨어 시스템이냐에 따라

새의 비행능력을 고려하지 않아도 되는 경우가 있다.

만약 새의 서식지와 먹이에 대한 응용프로그램이라면

처음 디자인한 클래스가 더욱 만족스러울 것이다.

즉, 프로그램의 요구 사항에 맞게 디자인 하는 것이 중요하다는 것이다.




이번에는 위의 펭귄문제를 런타임 에러를 통해 해결해보자.


void error(const std::string& msg);


class Penguin : public Bird {

public:

virtual void fly() { error ("Attempt to make a penguin fly!"); }

...

};


이 경우는 "펭귄은 날 수 없다." 가 아닌

"펭귄은 날 수 있다. 하지만 날려고 하면 에러가 난다" 이다.

"펭귄은 날 수 없다" 라는 것은 컴파일러가 판단할 수 있지만,

"날려고 하면 에러가 난다" 는 런타임에만 발견할 수 있다.




자 그럼 이번에는 컴파일 타임에서 이 문제를 해결해 보도록 하자.


class Bird{

...                                    // fly 없음

};


class Penguin : public Bird {

...                                    // fly 없음

};


Penguin p;

p.fly();                                    // 에러


이제 펭귄을 날려 보려고 하면, 컴파일 단계에서 문제가 생기게 된다.

런타임 에러를 내주는 것은 p.fly를 호출하는 것에 대해 문제가 없다.

런타임, 컴파일 타임 과연 어떤 해결책이 더 좋은 것일까?




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

좋은 인터페이스이다. 즉 , 펭귄의 비행을 컴파일 타임에 거부하는

설계가 그것을 런타임에 뒤늦게 알아내는 것보다 훨씬 좋다는 것이다.




새와 펭귄 문제는 이쯤하고, 사각형과 정사각형을 클래스의

상속 계통으로 만드는 문제로 넘어가 보자. 초등학교 수학시간에

배운 기억을 바탕으로 우리는 "정사각형은 직사각형이다."라는

사실을 알고 있을 것이다. 때문에 정사각형은 직사각형을

상속받도록 디자인 해야한다고 생각하는 것도 당연할 것이다.




하지만 직사각형은 넓이와 높이 모두 자유자재로 바뀌어도

직사각형이라는 것을 만족한다. 하지만 이를 상속받는

정사각형은 가로 새로가 항상 일치해야 한다는 조건이 있다.

때문에 직사각형에 가로 혹은 세로를 마음대로 조정하는

함수가 있다면, 정사각형에는 적용할 수 없는 함수이다.

때문에 이 케이스틑 is-a관계가 적용될 수가 없다.




여기서 중요한 부분은 public 상속은 기본 클래스 객체가

가진 모든 것들이 파생 클래스 객체에도 그대로 적용되는 상속이다.

때문에 직사각형과 정사각형은  public 상속으로 표현한다면

틀린 것이고, 컴파일 수준에서 무사히 통과과 되었다 해도

제대로 동작할 것이라는 보장은 없다.




코드가 컴파일된다는 것이 제대로 동작한다는 의미는 아니다.

이런 점 때문에 프로그래머는 계속 배움의 길을 개척해 가야한다.


Point!!

public 상속의 의미는 is-a 이다.

기본 클래스에 적용되는 모든 것들이

파생 클래스에 그대로 적용되어야 한다.

모든 파생 클래스 객체는 기본 클래스 객체의 일종이기 때문이다.




+ Recent posts