Is – a와 Has – a 관계
Fruit 클래스에는 과일의 무게와 칼로리 열량을 저장할 수 있다. 그리고 Fruit 클래스를 상속받는 Banana 클래스에는 Fruit 클래스 멤버들을 상속받을 수 있으므로 바나나 무게와 칼로리 열량을 저장하는 멤버들을 가질 것이다. 또한 일반적으로 과일에는 적용되지 않고 바나나에만 적용되는 바나나 연구소 껍질 지수와 같은, 새 멤버들을 Banana 클래스에 추가할 수 있다. 파생 클래스에 새로운 기능도 추가할 수 있으므로 그 관계를 is-a-kind-of로 표시하는 것이 더 정확하겠지만, 흔히 is-a 관계라는 말을 사용한다.
가장 일반적인 public 상속은 has-a 관계를 나타내지 않는다. 예를 들어, 점심이 과일을 가질 수는 있지만 일반적으로 점심은 과일이 아니다. 따라서 점심에 과일을 포함시키려는 시도로 Fruit 클래스로부터 Lunch 클래스를 파생시키면 안 된다. 점심에 과일을 넣는 것은 Lunch 클래스의 한 데이터 멤버로 Fruit 객체를 포함시키는 것이 가장 쉬운 방법이다. 이것이 바로 has-a 관계이다.
상속
프로그램이 파생 클래스의 객체를 생성할 때, 먼저 기초 클래스의 객체를 생성한다. 개념적으로 프로그램이 파생 클래스 생성자의 몸체 안으로 들어가기 전에, 기초 클래스 객체가 먼저 생성되어야 한다는 것을 의미한다. 따라서 C++에서는 멤버 초기자 리스트 문법을 사용한다.
예를 들어, 기초 클래스 APlayer 를 파생한 BPlayer 파생 클래스가 있을 때, BPlayer 생성자에 해당하는 코드는 다음과 같다. 여기서 파생 클래스 생성자가 기초 클래스 생성자에게 매개변수 fn, ln, ht를 전달한다.
BPlayer :: BPlayer ( int r, const char * fn, const string & ln, bool ht ) : APlayer (fn, ln, ht)
{ rating = r;
}
만약 멤버 초기자 리스트를 생략하면 디폴트 기초 클래스 생성자를 사용한다. 앞의 코드에서 사용자가 : APlayer (fn, ln, ht) 부분을 생략하면, 내부적으로는 : APlayer () 가 실행될 것이다.
반대로 파생 클래스의 객체가 수명을 다했을 때, 프로그램은 먼저 파생 클래스의 파괴자를 호출하고 그 다음 기초 클래스 파괴자를 호출한다.
- 기초 클래스 참조와 포인터가 파생 클래스 객체를 참조할 수 있다.
- 따라서 기초클래스 참조와 포인터를 매개변수로 사용하는 함수는, 기초 클래스 객체에도 사용할 수 있고 파생 클래스 객체에도 사용할 수 있다.
void Show(const BPlayer & rp) { … }
형식 매개변수 rp는 기초 클래스에 대한 참고이다. 따라서 rp는 기초 클래스 객체도 참조할 수 있고 파생 클래스 객체도 참조할 수 있다. 그래서 Show() 함수는 BPlayer 매개변수도 사용할 수 있고 APlayer 매개변수도 사용할 수 있다.
- 파생 클래스 참조나 포인터를 기초 클래스 참조나 포인터로 변환하는 것을 업캐스팅(upcasting)이라 한다. 따라서 Brass 클래스에서 파생된 BrassPlus 클래스에 대해서 다음과 같은 문법이 허용된다.
BrassPlus dilly(“Annie Dill”, 493222, 2000);
Brass * pb = &dilly;
Brass & rb = dilly;
- 기초 클래스 포인터나 참조를 파생 클래스 포인터나 참조로 변환하는 다운캐스팅(downcasting)은 업캐스팅과 다르게 명시적인 데이터 형 변환 없이는 허용되지 않는다.
Private 상속
has-a 관계를 구현하는 수단으로 사용되는 방법. Public 상속에서는 기초 클래스의 public 메서드가 파생 클래스에서 public 메서드가 되지만, private 상속에서는 기초 클래스의 public 메서드가 파생 클래스의 private 메서드가 된다. 즉, 파생 클래스가 기초 클래스의 인터페이스를 상속하지 않는다.
Class Student : private std :: string, private std :: valarray<double>
{ … };
- 기초 클래스의 public, protected 멤버가 p파생 클래스의 private 멤버가 된다.
- 클래스 안에 다른 클래스를 선언하는 containment는 ( has-a관계) , 객체를 이름이 있는 멤버 객체로 클래스에 추가하지만, private 상속은 객체를 이름이 없는 상속된 객체로 클래스에 추가한다. 이 두 가지 방법 모두 구현은 획득하지만 인터페이스는 획득하지 않는다. 두 접근 방식의 차이는 인터페이스가 아니라 구현에 영향을 준다.
- 다중 상속 가능
- 보통은 private 상속보다는 containment를 선호하지만, 상속을 사용하면 파생 클래스는 기초 클래스의 protected 멤버에 접근할 수 있고 또한 가상 함수를 재정의해야 할 필요가 있을 때 사용한다.
Protected 상속
기초 클래스의 public 멤버와 protected 멤버가 파생 클래스의 protected 멤버가 된다.
Class Student : private std :: string, private std :: valarray<double>
{ … };
- Private 상속과 Protected 상속의 주된 차이점 ?
'Dev Language > C++' 카테고리의 다른 글
C++로 DLL 만들기 (0) | 2017.03.08 |
---|---|
정적 결합과 동적 결합 & 가상함수 (0) | 2016.12.23 |
참조 변수 & 복사 생성자 (1) | 2016.12.06 |
if문과 switch문 (0) | 2016.12.06 |
함수 오버로딩 (0) | 2016.12.06 |