[Cpp] 클래스 관계 - 상속
객체 지향 프로그래밍은 하나의 클래스를 단독으로 사용하지 않는다.
각 클래스 간 관계가 존재하고 이를 분류하면 아래와 같다.
클래스 관계 | |||
상속(Inheritance) is - a |
연관(Association) is related to |
종속 (Dependency) uese - a |
|
소유(Aggregation) has - a |
구성(Composition) |
클래스 간 관계는 주로 UML을 이용하여 설명한다.
UML에서는 상속하는 클래스에서 상속받는 클래스로 화살표로 나타낸다.
예를 들어 강아지 클래스는 더 큰 범주인 동물 클래스를 상속받는다고 가정하면 아래 그림과 같이 나타낼 수 있다.
cpp에서는 위의 그림처럼 상속하는 클래스 즉 범용적인 클래스를 베이스클래스 또는 슈퍼 클래스라고 부르고, 구체적인 클래스를 파생클래스 또는 서브 클래스라고 부릅니다. 위 그림에서 보면 Dog는 파생클래스이고, Animal은 베이스 클래스가 되는 것을 알 수 있다.
일반적으로 구체적인 파생클래스는 범용적인 베이스 클래스보다 더 많은 특징을 갖고 있다.
예를 들면 강아지는 동물의 특징 외에도 다른 특징들이 존재한다. 이처럼 파생클래스는 베이스 클래스의 특징을 갖고 더 많은 특징을 추가하기 때문에 베이스클래스를 확장한 것 이라고 표현한다.
파생클래스는 베이스클래스의 모든 데이터 변수, 멤버변수를 가진 상태에서 추가 데이터 멤버와 멤버함수를 갖는다.
(다만 이때 생성자, 소멸자, 할당연산자는 상속받지 못한다.)
상속의 종류에는 public, protected, private의 3가지 가 있다. 하지만 실질적으로는 public을 제외한 나머지 상속을 잘 사용하지 않는다.
Cpp에서 상속을 받는 방법은 클래스 이름 뒤에 콜론(:)을 입력하고 접근제한자 (public, protected, private)와 베이스 클래스를 입력하여 만든다.
Animal 클래스를 상속받는 Dog 클래스를 cpp에서 작성하면 아래와 같다.
class Dog : public Animal{
// 클래스 내용
}
파생클래스는 베이스클래스의 모든 데이터 변수, 멤버변수를 갖는다고 앞서 설명했다.
하지만 파생클래스는 베이스 클래스에서 private로 선언된 데이터 멤버 변수에는 접근할 수 없다.
그러므로 이에 접근하기 위해서는 직접 접근하는 것이 아니라 getter/setter 와 같은 함수를 만들어 값을 간접적으로 접근할 수 있다.
void Person::setId(long id)
{
identity = id;
assert(identity >= 100000000 && identity <= 999999999) ;
}
void Student::setId(long id, double gp){
cout<<"오버라이딩된 setid"<<endl;
Person::setId(id); //위임 delegation
gpa = gp;
}
위 코드는 Student가 Person의 클래스를 상속받은 상태에서 Student에서 setId를 만드는 상황이다.
아 때 파생클래스에서 setId를 오버라이딩했을 때 상속받은 id는 private 변수이므로 직접 접근할 수 없으므로 public으로 생성된 Person::setId를 호출하여 id 멤버변수를 설정했다.
이처럼 파생클래스에서 베이스클래스 함수를 호출해서 어떤 작업을 하게 만드는 것을 위임(delegation) 이라고 한다.
상속되지 않는 멤버
앞서 설명한 대로 파생클래스는 베이스 클래스로부터 생성자, 소멸자 , 할당연산자는 상속되지 않는다.
왜냐하면 대부분 생성자는 해당 클래스의 데이터변수를 초기화하는 경우가 일반적이다.
하지만, private로 선언된 데이터변수는 파생클래스가 직접접근할 수 없으므로 초기화가 불가능하다.
소멸자에서도 마찬가지로 접근을 할 수 없기 때문에 상속받을 수 없다.
따라서 파생클래스에서 베이스클래스 생성자를 먼저 호출한 뒤에 파생클래스의 데이터 멤버를 초기화하는 방법으로 생성자를 사용할 수 있다. 소멸자도 생성자와 반대의 순서로 파생클래스에 데이터 멤버를 소멸한 뒤 베이스클래스의 소멸자를 호출하면 된다.