Part3) CH8. 상속과 다형성

songtofu·2022년 12월 12일
0

전문가를 위한 C

목록 보기
8/10

앞서

  • 객체와 객체에 상응하는 개념인 클래스 간의 관계 (상속, 다형성)
  • 확장, 상속 관계

8.1 상속

  • to-be 관계
  • 상속 관계 = 확장 관계: 기존의 객체나 클래스에 속성과 행위를 추가
  • 슈퍼타입 = 베이스 타입 = 부모 타입 ex.person_t
  • 자식 타입 = 상속받은 서브 타입 ex. student_t

8.1.1 상속의 본질

  • 상속 관계 = 본질적으로 합성관계 = 일대일 합성관계

    업캐스팅
    - 자식의 속성 구조체의 자료형에 대한 포인터를 부모 클래스의 속성 구조체의 자료형으로 변환하는 것

int main(int ac,char **av) {
	student_t s;
    student_t* s_ptr = &s;
    person_t* p_ptr = (person_t*)&s;
}
  • s_ptr과 p_ptr 포인터는 메모리에서 같은 주소를 가리킬 것.
  • 이는 student_t형의 구조체 변수가 해당 메모리 레이아웃에서 실제로는 person_t 구조체를 상속한다는 의미. 그러므로 student 객체를 가리키는 포인터로 Person 클래스의 함수 행위를 사용 가능 = student 객체에서 Person 클래스의 행위 함수를 재사용할 수 있음.
struct person_t;

typedef struct {
	struct person_t perosn; //오류발생!!
    char ~~;
    unsigned int ~~~;
}	student_t;
  • 포인터는 불완전한 형식에 관한 것. 변수에 관한 것 X.
  • 불완전한 형식에는 힙 메모리 할당 X.
  • 캡슐화에 대해 학습한 내용대로 student_t 구조체는 person_t의 실제 정의를 알아야함. person_t 구조체는 비공개여야함. 클래스에는 보이지 않아야 함.

상속 관계를 구현할 때 두 가지 접근 방법
1. 자식 클래스가 베이스 클래스에 대한 비공개 구현(실제 정의)에 접근할 수 있도록 하기
2. 자식 클래스가 베이스 클래스의 공용 인터페이스에만 접근할 수 있도록 하기.

C의 상속에 관한 첫 번째 접근법

  • Person 클래스의 공용 인터페이스 헤더 파일에서는 person_t에 관한 실제 정의 포함 X.
  • person_t의 실제 정의를 포함하는 비공개 헤더 파일을 만들어야함.
  • Student 클래스의 공용 인터페이스에는 행위 함수를 person 객체에 없는 것만 만든다. student 객체에서도 person 클래스의 행위 함수를 사용할 수 있기 때문.
  • Student 클래스의 비공개 정의.c 에는 Person 클래스의 비공개 헤더를 포함해야 함.
  • 반드시 구조체의 첫 번째 필드여야 함. 그렇지 않으면 Person 클래스의 행위 함수를 사용하지 못하게 된다.

소멸은 자식 수준 -> 부모 수준 으로 일어나야함
(p.s. 소멸은 생성의 순서와 반대로 일어나야함. 이 케이스에선 자식->부모가 맞음)

C의 상속에 관한 두 번째 접근법

  • 두 번째 접근 방법을 사용해서 부모의 구조체 변수를 가리키는 포인터
  • 이 방식으로 자식 클래스는 부모 클래스의 구현과 독립적이다. 정보 은닉에 좋음!

    첫 번째 방식과 두 번째 방식은 출력 결과와 실제 결과의 측면에서 비슷. 그러나, Student 클래스는 Person 클래스의 공용 인터페이스에만 의존, 비공개 정의에는 의존하지 않는다는 점이 주된 차이이다. 이는 클래스를 분리 하는 것 이므로 자식 클래스의 구현을 변경하지 않고도 부모 클래스의 구현을 쉽게 바꿀 수 있으므로 아주 유용~~!

  • 1, 2번의 근본적인 차이 O: Person 클래스는 같은 공용 인터페이스를 가짐. 하지만 Student 클래스의 경우에는 변경되어한다.
  • Sutdent 클래스는 Person 클래스에서 선언한 모든 행위 함수를 반복해야함. student_t 포인터를 person_t 포인터로 더 이상 변환할 수 없기 때문. = Student 및 Person 포인터에 대해 더 이상 업캐스팅을 할 수 없음.
  • person_t의 비공개 정의 소스 파일 내부에 더 이상 비공개 헤더 파일 사용X = Student 클래스와 같은 다른 클래스로 정의를 전혀 공유하지 않겠다는 의미.
  • 참고: 속성 구조체에서 이 포인터 필드가 첫 번재 항목 될 필요 X. 서로의 포인터는 더 이상 상호 변환할 수 없고, 메모리에서 다른 주소를 가리키며 인접한 주소도 아님.
  • 상속받은 속성을 읽기 위해 Person 클래스의 행위를 사용할 수 없으므로 Student 클래스는 상속받은 속성 및 비공개 속성을 노출하기 위해 자신의 행위 함수들을 노출해야함. = Student 클래스 내부에 있는 부모인 person 객체의 비공개 속성을 노출하기 위해 Student 클래스는 래퍼 함수를 노출해야함.
  • Student 객체 그 자체로는 Person 객체의 비공개 속성에 대해 아무것도 알지 못함. (첫번째와 다름)

8.2 다형성

  • 다형성? 서로 다른 행위를 갖는 같은 코드를 두는 기법 = 같은 공용 인터페이스(또는 행위 함수의 집합)를 사용해 다른 행위를 갖는다.
  • 다형성을 통해 전체 코드베이스를 다시 컴파일하지 않고도 코드를 상속하거나 기능을 추가 할 수 있음.

8.2.1 다형성 소개

  • 다형성 메커니즘의 이점 활용을 위해 C에서 상속을 구현하는 첫 번재 접근법을 사용한다는 의미.

8.2.2 다형성이 필요한 이유

  • 베이스 타입에 관한 여러 서브타입을 가지고 코드를 사용할 때 조차도 코드를 '그대로'두기를 원함

8.2.3 C에서 다형적 행위를 갖는 방법

  • 첫번째 접근법을 사용. 함수 포인터를 이용할 수 있음. 하지만 이러한 함수 포인터를 속성 구조체 내의 필드에 둬야함.
typedef void (*sound_func_t)(void*);

typedef struct {
	char* name;
    sound_func_t sound_func; // 이 멤버는 실제 sound 행위를 수행하는 함수에 대한 포인터입니다.
}
  • 생성자 animal_ctor 내부에서 animal_sound의 주소를 animal 객체의 sound_func 필드에 저장. sound_func가 함수 포인터라는 점에 주의. 모든 자식 객체는 이 함수 포인터를 상속. 이 함수 포인터는 animal_sound의 기본 정의를 가리킴.
  • 행위 함수 animal_sound 내부에서는 sound_func 필드에서 포인터가 가리키는 함수를 호출. = sound_func는 이전 예제에서 __animal_sound에 해당하는 sound 행위의 실제 정의를 가리키는 함수 포인터 필드이다.
  • sound_func가 다른 함수를 가리킬 떄, animal_sound를 불러오면 sound_func가 가리키는 다른 함수가 호출된다.
    -> 이것이 행위 sound에 대한 기본 정의를 오버라이딩하기 위해 Cat과 Duck 클래스에서 사용하게 될 방식.
  • C++ 같은 언어에서 다형적인 함수임을 나타내는 특정한 키워드 사용 (virtual 함수) 가상 함수는 자식 클래스에서 오버라이딩할 수 있는 행위 함수이다.
profile
읽으면 머리에 안들어와서 직접 쓰는 중. 잘못된 부분 지적 대환영

0개의 댓글