Derived
클래스를 Base
클래스로부터 public 상속을 통해 파생시켰다.
= Derived
객체는 Base
객체이다.
즉, 상대적으로 Base
는 일반적인 개념이며, Derived
는 특수한 개념이다.
따라서 Base
타입을 쓰는 객체 대신에 Derived
객체를 사용할 수 있다.
void eat(const Person& p); // 사람은 먹는다
void study(const Student& a); // 학과 공부는 학생만 한다
Person p;
Student s;
eat(p); // Success.
eat(s); // Success.
study(s); // Success.
study(p); // Error. p는 Student가 아니다
class Bird
{
public:
virtual void fly(); // 새는 날 수 있다
...
};
class Penguin : public Bird // 펭귄은 새다
{
...
};
is-a 관계에만 집중해서, 펭귄은 새의 일종이므로 Penguin
가 Bird
를 public 상속했다.
그런데 펭귄은 날 수 없다.
class Bird
{
... // fly 함수 선언 X
};
class FlyingBird : public Bird
{
public:
virtual void fly(); // fly 함수 선언 O
...
};
class Penguin : public Bird
{
... // fly 함수 선언 X
};
날 수 있는 새와 날지 못하는 새를 구분하도록 클래스를 설계해서 해결할 수 있다.
다만 이런 설계는 이 소프트웨어 시스템이 새의 비행 능력에 관심이 있을 때나 의미가 있다.
만약 새의 부리와 날개에만 관심이 있는 시스템이라면 굳이 비행 능력에 따라 구분할 필요가 없다.
최고의 설계는 제작하려는 소프트웨어 시스템이 기대하는 바에 따라 달라진다!
void error(string& msg);
class Penguin : public Bird
{
public:
virtual void fly() { error("펭귄은 못날아!"); }
...
};
펭귄이 날면 런타임 에러가 나게 만들었다.
이 코드의 의미는 "펭귄은 날 수 있지만 펭귄이 실제로 날려고 하면 에러가 난다" 이다.
사용자가 바랬을 "펭귄은 날 수 없다"가 아니다.
class Bird
{
... // fly 함수 선언 X
};
class Penguin : public Bird
{
... // fly 함수 선언 X
};
Penguin p;
p.fly(); // Error.
유효하지 않은 코드는 컴파일 단계에서 막아주는 인터페이스가 좋은 인터페이스다.
즉, 런타임보다 컴파일에서 에러가 발생하도록 하는 설계가 더욱 좋다. 항목 18
// 직사각형
class Rectangle
{
public:
virtual void setHeight(int newHeight);
virtual void setHeight(int newHeight);
virtual int height() const;
virtual int width() const;
...
};
// 정사각형
class Square : public Rectangle { ... };
// 직사각형의 가로 길이만 변경하는 함수
void makeBigger(Rectangle& r)
{
int oldHeight = r.height();
r.setWidth(r.width() + 10);
assert(r.height() == oldHeight);
}
Square s;
...
assert(s.width() == s.height());
makeBigger(s);
assert(s.width() == s.height());
makeBigger
함수를 호출한 이후 s
의 가로 길이가 늘어난다.
따라서 두 번째 assert
문이 실패한다.
이런 경우 애초에 직사각형-정사각형 관계를 public 상속을 써서 표현하면 안된다.
"가로 길이가 세로 길이에 상관없이 바뀔 수 있다"는 직사각형의 성질이 "가로와 세로 길이가 같아야 한다"는 정사각형에 적용할 수 없기 때문이다.
public 상속
- is-a(...는 ...의 일종)이라는 의미
- 기본 클래스에 적용되는 모든 것들이 파생 클래스에 그대로 적용되어야 함