// is a
class Manager : public Employee // Manager is a Employee
// 클래스는 파생될수록 좀 더 구체화된다.
// 부모 클래스로 올라갈수록 일반화된다.
class BankAccount{};
class CheckingAccount : public BankAccount
class SavingAccount : public BankAccount
// 모든 관계는 is a로만 표현은 불가능하다.
// has a
class Car
{
private:
Engine e;
Brake b;
};
#include <iostream>
#include <string>
class Base
{
std::string s;
public:
Base() : s("기반") { std::cout << "기반 클래스" << std::endl; }
void what() { std::cout << s << std::endl; }
};
class Derived : public Base
{
std::string s;
public:
Derived() : s("파생"), Base() { std::cout << "파생 클래스" << std::endl; }
void what() { std::cout << s << std::endl; }
};
int main()
{
Base p;
Derived c;
std::cout << "=== 포인터 버전 ===" << std::endl;
Base* p_c = &c; // 업캐스팅
p_c->what();
return 0;
}
실행 결과
기반 클래스
기반 클래스
파생 클래스
=== 포인터 버전 ===
기반
Derived 객체 c는 Base를 상속받은 객체이기 때문에 Base 객체를 가리키는 포인터가
c를 가리켜도 문제 없음 -> 업 캐스팅
#include <iostream>
#include <string>
class Base
{
std::string s;
public:
Base() : s("기반") { std::cout << "기반 클래스" << std::endl; }
void what() { std::cout << s << std::endl; }
};
class Derived : public Base
{
std::string s;
public:
Derived() : s("파생"), Base() { std::cout << "파생 클래스" << std::endl; }
void what() { std::cout << s << std::endl; }
};
int main()
{
Base p;
Derived c;
std::cout << "=== 포인터 버전 ===" << std::endl;
Derived* p_p = &p;
p_p->what();
return 0;
}
컴파일 오류
error C2440: 'initializing' : cannot convert from 'Base *' to 'Derived *'
Derived*가 Base 객체를 가리키면 p_p->what() 호출 시 Derived의 what() 함수가
호출되어야 하는데 Base 객체는 Derived에 대한 정보가 없으므로 문제가 됨
-> 다운 캐스팅을 할 때는 주의가 필요
#include <iostream>
class Base {
public:
Base() { std::cout << "기반 클래스" << std::endl; }
virtual void what() { std::cout << "기반 클래스의 what()" << std::endl; }
};
class Derived : public Base {
public:
Derived() : Base() { std::cout << "파생 클래스" << std::endl; }
void what() { std::cout << "파생 클래스의 what()" << std::endl; }
};
int main() {
Base p;
Derived c;
Base* p_c = &c;
Base* p_p = &p;
std::cout << " == 실제 객체는 Base == " << std::endl;
p_p->what();
std::cout << " == 실제 객체는 Derived == " << std::endl;
p_c->what();
return 0;
}
실행 결과
기반 클래스
기반 클래스
파생 클래스
== 실제 객체는 Base ==
기반 클래스의 what()
== 실제 객체는 Derived ==
파생 클래스의 what()
이전에는 Base*로 Derived 객체를 가리키는 경우 Base의 what()함수가 호출되었으나
virtual 키워드를 사용하면 Derived의 what() 함수가 호출됨
virtual 키워드가 붙은 함수를 가상 함수라고 부름
p_p->what();
"흠, p_c 는 Base 포인터니까 Base 의 what() 을 실행해야지"
"어 근데 what 이 virtual 이네?"
"잠깐. 이거 실제 Base 객체 맞어? 어 맞네."
"Base 의 what 을 실행하자"
p_c->what();
"흠, p_c 는 Base 포인터니까 Base 의 what() 을 실행해야지"
"어 근데 what 이 virtual 이네?"
"잠깐. 이거 실제 Base 객체 맞어? 아니네 Derived 객체네"
"그럼 Derived 의 what 을 실행해야지"
이렇게 컴파일 시에 어떤 함수가 실행될 지 정해지지 않고 런타임 시에 정해지는 일을
동적 바인딩이라고 부름
#include <iostream>
#include <string>
class Employee
{
protected:
std::string name;
int age;
std::string position; // 직책 (이름)
int rank; // 순위 (값이 클 수록 높은 순위)
public:
Employee(std::string name, int age, std::string position, int rank)
: name(name), age(age), position(position), rank(rank) {}
// 복사 생성자
Employee(const Employee& employee)
{
name = employee.name;
age = employee.age;
position = employee.position;
rank = employee.rank;
}
// 디폴트 생성자
Employee() {}
virtual void print_info()
{
std::cout << name << " (" << position << " , " << age << ") ==> "
<< calculate_pay() << "만원" << std::endl;
}
virtual int calculate_pay() { return 200 + rank * 50; }
};
class Manager : public Employee
{
int year_of_service;
public:
Manager(std::string name, int age, std::string position, int rank,
int year_of_service)
: year_of_service(year_of_service), Employee(name, age, position, rank) {}
int calculate_pay() override { return 200 + rank * 50 + 5 * year_of_service; }
void print_info() override
{
std::cout << name << " (" << position << " , " << age << ", "
<< year_of_service << "년차) ==> " << calculate_pay() << "만원"
<< std::endl;
}
};
class EmployeeList
{
int alloc_employee; // 할당한 총 직원 수
int current_employee; // 현재 직원 수
Employee** employee_list; // 직원 데이터
public:
EmployeeList(int alloc_employee) : alloc_employee(alloc_employee)
{
employee_list = new Employee*[alloc_employee];
current_employee = 0;
}
void add_employee(Employee* employee)
{
// 사실 current_employee 보다 alloc_employee 가 더
// 많아지는 경우 반드시 재할당을 해야 하지만, 여기서는
// 최대한 단순하게 생각해서 alloc_employee 는
// 언제나 current_employee 보다 크다고 생각한다.
// (즉 할당된 크기는 현재 총 직원수 보다 많음)
employee_list[current_employee] = employee;
current_employee++;
}
int current_employee_num() { return current_employee; }
void print_employee_info()
{
int total_pay = 0;
for (int i = 0; i < current_employee; i++) {
employee_list[i]->print_info();
total_pay += employee_list[i]->calculate_pay();
}
std::cout << "총 비용 : " << total_pay << "만원 " << std::endl;
}
~EmployeeList()
{
for (int i = 0; i < current_employee; i++)
{
delete employee_list[i];
}
delete[] employee_list;
}
};
int main()
{
EmployeeList emp_list(10);
emp_list.add_employee(new Employee("노홍철", 34, "평사원", 1));
emp_list.add_employee(new Employee("하하", 34, "평사원", 1));
emp_list.add_employee(new Manager("유재석", 41, "부장", 7, 12));
emp_list.add_employee(new Manager("정준하", 43, "과장", 4, 15));
emp_list.add_employee(new Manager("박명수", 43, "차장", 5, 13));
emp_list.add_employee(new Employee("정형돈", 36, "대리", 2));
emp_list.add_employee(new Employee("길", 36, "인턴", -2));
emp_list.print_employee_info();
return 0;
}
실행 결과
노홍철 (평사원 , 34) ==> 250만원
하하 (평사원 , 34) ==> 250만원
유재석 (부장 , 41, 12년차) ==> 610만원
정준하 (과장 , 43, 15년차) ==> 475만원
박명수 (차장 , 43, 13년차) ==> 515만원
정형돈 (대리 , 36) ==> 300만원
길 (인턴 , 36) ==> 100만원
총 비용 : 2500만원
Employee*가 가리키고 있음에도 불구하고 Manager는 Manager 함수를
Employee는 Employee의 함수를 호출함
하나의 메소드를 호출했음에도 불구하고 여러가지 다른 작업들을 하는 것을 다형성이라고 부름