TL;DR
이 글은 씹어먹는 C++ 6-1의 내용을 정리 및 요약한 글입니다.
사원 관리 프로그램
회사 사원들의 월급을 계산해서 한달에 총 얼마를 월급으로 지출하는 지 알려주는 프로그램을 만드려고 한다. 사원들의 필요한 데이터는 이름, 나이, 직책, 직책 순위로 구성된다.
class Employee{
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(){}
void print_info(){
std::cout << name << "(" << position << "," << age << ") ==>" <<calculate_pay() << "만원" << std::endl;
}
int calculate_pay(){return 200+rank*50;}
}
Employee를 만들었으니 Employee 객체들을 관리할 수 있는 EmployeeList 클래스를 만들어보자.
int alloc_employee; // 할당한 총 직원 수
int current_employee; // 현재 직원 수
Employee **employee_list; // 직원 데이터
이 변수들을 통해 사원 데이터를 처리할 것이다. alloc_employee는 할당된 크기를 알려주는 배열이고 curren_employee는 현재 employee_list에 등록된 사원 수이다. employee_list가 Employee** 타입인 이유는 이를 Employee* 객체를 담는 배열로 사용할 것이기 때문이다.
EmployeeList(int alloc_employee) : alloc_employee(alloc_employee){
employee_list=new Employee*[alloc_employee];
current_employee=0;
}
cf). 이부분에 대해서 new Employee*[alloc_employee]라는 코드는 현대에는 잘 쓰이지 않는다. 현대에는 스마트 포인터와 vector를 사용하여 코드를 작성한다.
사원을 추가하는 함수는 아래처럼 구성한다.
void add_employee(Employee* employee){
employee_list[current_employee]=employee;
current_employee++;
}
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){
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 Employee("유재석", 41, "부장", 7));
emp_list.add_employee(new Employee("정준하", 43, "과장", 4));
emp_list.add_employee(new Employee("박명수", 43, "차장", 5));
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) ==>550만원
정준하(과장,43) ==>400만원
박명수(차장,43) ==>450만원
정형돈(대리,36) ==>300만원
길(인턴,36) ==>100만원
총 비용: 2300만원
여기에서 차장 이상 급은 관리 데이터에 근속 년수를 포함시켜서 월급에 추가해달라는 요청사항이 생긴다. 이를 위해 매니저 클래스를 만든다.
class Manager{
std::string name;
int age;
std::string position;
int rank;
int year_of_service;
public:
Manage(std::string name, int age, std::string position, int rank, int year_of_service) : year_of_service(year_of_service), name(name), age(age), position(position), rank(rank) {}
Manager(const Manager& manager){
name=manager.name;
age=manager.age;
position=manager.position;
rank=manager.rank;
year_of_service=manager.year_of_service;
}
Manager(){}
int calculate_pay(){return 2-+rank*50+5*year_of_service;}
void print_info(){
std::cout << name << "(" << position << ", " << age << ", " << year_of_service << "년차) ==>" << calculate_pay() << "만원" << std::endl;
}
}
기존의 Employee 클래스와 같고 year_of_service만 추가 되었다. 그리고 이를 위해 employee_list와 manage_list를 따로 구분해야 한다.
상속(Inheritance)
C++에서는 상속을 통해 다른 클래스의 내용을 그대로 포함할 수 있는 작업을 가능하게 해준다. 상속을 통해 다른 클래스의 정보를 물려받아서 사용할 수 있다.
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() : Base(), s("파생"){
std::cout << "파생 클래스" << std::endl;
what();
}
}
class Derived : public Base는 Derived가 Base를 public 형식으로 상속받겠다는 의미가 된다.
따라서 Derived 클래스에서 what을 호출할 수 있다.
Derived의 생성자는 초기화 시트르에서 기반 생성자를 호출해서 기반 생성을 먼저 한 후 Derived의 생성자가 실행되어야 한다. 기반 클래스의 생성자를 명시적으로 호출하지 않을 경우 기반 클래스의 디폴트 생성자가 호출된다.
#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() : Base(), s("파생"){
std::cout << "파생 클래스" << std::endl;
what();
}
};
int main(){
std::cout << "=== 기반 클래스 생성 ===" << std::endl;
Base p;
std::cout << "=== 파생 클래스 생성 ===" << std::endl;
Derived c;
return 0;
}
=== 기반 클래스 생성 ===
기반 클래스
=== 파생 클래스 생성 ===
기반 클래스
파생 클래스
기반
기반 클래스가 생성될 때는 정상적으로 기반 클래스라고 출력된다.
파생 클래스가 생성될 때는 Base가 먼저 초기화 되면서 기반 클래스가 출력되고 그 다음 파생 클래스가 출력되고 what을 호출하게 된다. what을 호출했을 때 기반이 출력된 이유는 what 함수는 Base에 정의되어 있기 때문에 Base의 s가 출력되어 기반이라고 나오는 것이다.
그렇다면 만약 Derived에서 what을 정의해준다면?
#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() : Base(), s("파생"){
std::cout << "파생 클래스" << std::endl;
what();
}
void what(){std::cout << s << std::endl;}
};
int main(){
std::cout << "=== 기반 클래스 생성 ===" << std::endl;
Base p;
std::cout << "=== 파생 클래스 생성 ===" << std::endl;
Derived c;
return 0;
}
=== 기반 클래스 생성 ===
기반 클래스
=== 파생 클래스 생성 ===
기반 클래스
파생 클래스
파생
이번에는 Derived 클래스에 Base와 같은 기능을 하는 what을 정의하였다. 이 경우 Derived를 초기화 할 때 what을 호출하면 Derived의 what을 호출하게 되어 파생을 출력하게 된다.
이런 것을 가리켜 오버라이딩이라고 한다. Derived의 what 함수가 Base의 what 함수를 오버라이딩 한 것이다.
protected
기본적으로 private 멤버 변수 혹은 멤버 함수는 어떠한 경우에서도 자기 클래스 말고는 접근할 수 없다. 하지만 종종 파생 클래스에서 기반 클래스의 데이터에 직접 접근할 필요성이 있다. 예를 들면 위에서 Employee 클래스를 기반으로 하여 Manager 클래스를 만든다면 name이나 age에 접근할 필요성이 있다. C++에서는 private과 public의 중간인 protected를 이용하여 중간 위치에 있는 접근 지시자를 지원한다. 이 키워드는 상속 받는 클래스에서는 접근 가능하고 그 외의 곳에서는 접근 불가능하다.
class Base{
protected:
std::string parent_string;
public:
Base() : parent_string("기반"){std::cout << "기반 클래스" << std::endl;}
void what(){std::cout << parent_string << std::endl;}
};
class Derived : public Base{
std::string child_string;
public:
Derived() : Base(), child_string("파생"){
std::cout << "파생 클래스" <<std::endl;
parent_string="바꾸기";
}
void what(){std::cout << child_string << std::endl}
};
위 코드에서 protected로 선언된 parent_string은 파생 클래스인 Derived에서 접근이 가능하다.
또한 class Derived : public Base에서
- public으로 상속받았다면 접근 지시자들에 영향 없이 그대로 작동한다.
- protected로 상속하였다면 public은 protected로 바뀌고 나머지는 그대로 유지된다.
- private으로 상속하였다면 모든 접근 지시자들이 private이 된다.
사원 관리 프로그램에 적용하기
#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(){}
void print_info(){
std::cout << name << "(" << position << ", " << age << ")==>"<< calculate_pay() << "만원" << std::endl;
}
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):Employee(name, age, position, rank), year_of_service(year_of_service){}
Manager(const Manager& manager):Employee(manager.name, manager.age, manager.position, manager.rank){
year_of_service=manager.year_of_service;
}
Manager():Employee(){}
int calculate_pay(){return 200+rank*50+5*year_of_service;}
void print_info(){
std::cout << name << "(" << position << ", " << age << ", " << year_of_service << "년차)==>" << calculate_pay() << "만원" << std::endl;
}
};
class EmployeeList{
int alloc_employee;
int current_employee;
int current_manager;
Employee** employee_list;
Manager** manager_list;
public:
EmployeeList(int alloc_employee):alloc_employee(alloc_employee){
employee_list=new Employee*[alloc_employee];
manager_list=new Manager*[alloc_employee];
current_employee=0;
current_manager=0;
}
void add_employee(Employee* employee){
employee_list[current_employee]=employee;
current_employee++;
}
void add_manager(Manager* manager){
manager_list[current_manager]=manager;
current_manager++;
}
int current_employee_num(){return current_employee+current_manager;}
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();
}
for(int i=0;i<current_manager;i++){
manager_list[i]->print_info();
total_pay+=manager_list[i]->calculate_pay();
}
std::cout << "총 비용: " << total_pay << "만원" << std::endl;
}
~EmployeeList(){
for(int i=0;i<current_employee;i++){
delete employee_list[i];
}
for(int i=0;i<current_manager;i++){
delete manager_list[i];
}
delete[] employee_list;
delete[] manager_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_manager(new Manager("유재석", 41, "부장", 7, 12));
emp_list.add_manager(new Manager("정준하", 43, "과장", 4, 15));
emp_list.add_manager(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만원
정형돈(대리, 36)==>300만원
길(인턴, 36)==>100만원
유재석(부장, 41, 12년차)==>610만원
정준하(과장, 43, 15년차)==>475만원
박명수(차장, 43, 13년차)==>515만원
총 비용: 2500만원
댓글
Discussion 원문