C++ Virtual function.
1.C++ ls - a and has - a
일단 이야기를 진행하기 전에, 어떠한 경우에서 상속을 사용하는지 생각해본다. C++ 에서 상속을 도입한 이유는 단순히 똑같은 코드를 또 쓰는 것을 막기 위한 Ctrl + C, Ctrl + V 방지용으로 위한 것이 아니다. 실제 이유는 상속이라는 기능을 통해서 객체지향프로그래밍에서 추구하는 실제 객체의 추상화를 좀 더 효과적으로 할 수 있게 되었다.
상속이란 것이 없던 C 언어에서는 어떠한 구조체 사이의 관계를 표현할 수 있는 방법이 없었다. 하지만 C++ 에서 상속이란 것을 도입함으로써, 클래스 사이에 관계를 표현할 수 있게 되었는데, 예를 들어서 Manager 가 Employee 를 상속한다.
class Manager : public Employee
→ Manager 클래스는 Employee 의 모든 기능을 포함한다.
→ Manager 클래스는 Employee 의 기능을 모두 수행할 수 있기 때문에 (Manager 에게는 약간 기분 나쁘겠지만) Manager 를 Employee 라고
칭해도 무방하다 .
→ 즉, 모든 Manager 는 Employee.
→ "Manager is a Employee !!"
따라서, 모든 상속 관계는 'is a' 관계라고 볼 수 있다. 당연한 점은, 이를 뒤바꾸면 성립되지 않는다. 즉 Manager 는 Employee 이지만 Employee 는 Manager 가 아니다. 이렇기에, Manager 를 Employee 로 부를 수 있지만, Employee 는 Manager 로 부를 수 없다.
프로그램 설계 시에 클래스들 간의 상속 관계를 도표로 나타내는 경우가 종종 있는데, 많은 경우 파생 클래스가 부모 클래스를 화살표로 가리키게 그린다.
실제 세상에서 is a 관계로 이루어진 것들은 수 없이 많다.
바로 클래스가 파생되면 파생될 수록 좀 더 특수화 (구체화;specialize) 된다는 의미다. 즉, Employee 클래스가 일반적인 사원을 위한 클래스 였다면Manager 클래스 들은 그 일반적인 사원들 중에서도 좀 더 특수한 부류의 사원들을 의미하게 된다. 또, BankAccount 도 일반적인 은행 계좌를 위한 클래스 였다면, 이를 상속 받는 CheckingAccount, SavingsAccount 들은 좀 더 구체적인 클래스가 된다. 반대로, 부모 클래스로 거슬러 올라가면 올라갈 수 록 좀 더 일반화 (generalize) 된다고 한다.
그렇다면 모든 클래스들의 관계를 is - a 로만 표현할 수 있을까? 아니다. 어떤 클래스들 사이에서는 is - a 대신에 has - a 관계가 성립하기도 한다. 예를 들어서, 간단히 자동차 클래스를 생각해보면, 자동차 클래스를 구성하기 위해서는 엔진 클래스, 브레이크 클래스, 오디오 클래스 등 수 많은 클래스들이 필요하다. 그렇다고 이들 사이에 is a 관계를 도입 할 수 없다. (자동차 is a 엔진? 자동차 is a 브레이크?) 그 대신, 이들 사이는 has - a 관계로 쉽게 표현할 수 있다.
즉, 자동차는 엔진을 가진다 (자동차 has a 엔진), 자동차는 브레이크를 가진다 (자동차 has a 브레이크). 이런 has - a 관계는 우리가 흔히 해왔듯이 다음과 같이 클래스로 나타내면 된다.
class Car ===============Q
{
private:
Engine e;
Brake b; // break 아니다 :)
....
};
=================================
class EmployeeList========================W
{
int alloc_employee; // 할당한 총 직원 수
int current_employee; // 현재 직원 수
Employee **employee_list; // 직원 데이터
Parent* p_c = &c;
그 대신 p 는 엄연한 Parent 객체를 가리키는 포인터. 따라서, p 의 what 을 실행한다면 p 는 'Parent 의 what 을 실행해 주어야 겠구나' 하고, Parent의 what을 실행해서, Parent의 what은 Parent 의 s를 출력 하게 된다. 따라서 위처럼 '부모' 가 출력된다.
이러한 형태의 캐스팅을 파생 클래스에서 부모 클래스로 캐스팅 하는 것을 업 캐스팅이라고 한다.
위 그림을 보면 왜 업 캐스팅이라 부르는 지 알 수 있을 것이다.
반대인 다운 캐스팅도 알아보자.
만일 Child* 포인터가 Parent 객체를 가리킨다고 해보자. 그렇다면 p_p->what() 하게 된다면 Child 의 what 함수가 호출되어야만 하는데, 이는 불가능하다. 왜냐하면 p_p 가 가리키는 객체는 Parent 객체 이므로 Child 에 대한 정보가 없다. 따라서, 이와 같은 문제를 막기 위해서 컴파일러 상에서 함부로 다운 캐스팅 하는 것을 금지하고 있다.
만약에 강제적으로 다운 캐스팅을 하는 경우, 컴파일 타임에서 오류를 찾아내기 매우 힘들기 때문에 다운 캐스팅은 작동이 보장되지 않는 한 좋지 않다.
2.C++ Virtual keyword
p_c->what();
p_p->what();
// i 는 입력받는 변수.
if(i == 1) {
p_p = &c;
}
else {
p_p = &p;
}
p_p->what();
cf) 다형성(Polyomorphism) ==> 같은 print_info함수를 호출했음에도 불구하고, 어떤 경우는 Employee의 것, 다른 경우는 Manager의 것
즉 하나의 메소드를 호출했음에도 불구하고, 여러가지 다른 작업들을 하는 것.
'#Programming Language > C++' 카테고리의 다른 글
C++ I / O in C++ (0) | 2018.04.01 |
---|---|
C++ Virtual functions and inheritance. (0) | 2018.04.01 |
C++ Standard String & Inheritance. (0) | 2018.04.01 |
C++ Various overloading part 2. (0) | 2018.04.01 |
C++ Various overloading. (0) | 2018.04.01 |