728x90
728x90

IS-A 관계에 대해 계속 설명한다.

 

객체 포인터 변수

객체의 주소 값을 저장하는 포인터 변수
클래스를 기반으로도 포인터 변수를 선언할 수 있다.

Person* ptr; //포인터변수 선언
ptr = new Person(); //포인터 변수의 객체 참조

Person형 포인터는 Person 객체, Person을 상속하는 유도 클래스의 객체도 가리킬 수 있다.

class Student: public Person
{ .... } ;
Person* ptr = new Student();

Student 클래스를 상속하는 유도 클래스 PartTimeStudent가 다음의 형태로 정의되었다고 가정하면
Person형 포인터변수는 PartTimeStudent 객체도 가리킬 수 있다.

class PartTimeStudent: public Student
{ ..... };
Person* ptr = new PartTimeStudent();

즉, C++에서 AAA형 포인터 변수는 AAA 객체 또는 AAA를 직접/간접적으로 상속하는 모든 객체를 가리킬 수 있다. (객체의 주소값을 저장할 수 있다.)

 

// ObjectPointer.cpp
#include <iostream>
using namespace std;

class Person
{
public:
    void Sleep() {
        cout<<"Sleep"<<endl;
    }
};

class Student: public Person
{
public:
    void Study() {
        cout<<"Study"<<endl;
    }
};

class PartTimeStudent: public Student
{
public:
    void Work() {
        cout<<"Work"<<endl;
    }
};

int main()
{
    Person* ptr1 = new Student();
    Person* ptr2 = new PartTimeStudent();
    Student* ptr3 = new PartTimeStudent();
    ptr1->Sleep();
    ptr2->Sleep();
    ptr3->Study();
    delete ptr1; delete ptr2; delete ptr3;
    return 0;
}

 

 

Student *s가 Person 클래스의 객체를 가리키는 건 문제가 된다. 역방향은 성립하지 않기 때문이다 (IS-A가 아님)

 

유도클래스의 객체까지 가리킬 수 있다

C++에서 AAA형 포인터 변수는 AAA 객체 또는 AAA를 직접/간접적으로 상속하는 모든 객체를 가리킬 수 있다. (객체의 주소값을 저장할 수 있다.) Student, PartTimeStudent 객체는 Person객체의 일종이기 때문이다. 

 

전 단원의 급여관리 예제 확장 + 함수오버라이딩

정규직, 영업직, 임시직 모두 고용인이며 영업직은 정규직의 일종이다.
EmployeeHandler 클래스가 저장,관리 하는 대상이 Employee 객체가 되면 이후 Employee 클래스를 직간접적으로 상속하는 클래스가 추가되어도 EmployeeHandler 클래스는 변화가 없다.

// EmployeeManager2.cpp

#include <iostream>
#include <cstring>
using namespace std;

class Employee // 고용인
{
private:
    char name[100]; //고용인의 이름
public:
    Employee(char* name)
    {
        strcpy(this->name, name);
    }
    void ShowYourName() const
    {
        cout<<"name : "<<name<<endl;
    }
};

class PermanentWorker: public Employee 
{
private:
    int salary; //월급여
public:
    PermanentWorker(char* name, int money)
        : Employee(name), salary(money)
    {    }
    int GetPay() const
    {
        return salary;    
    }
    void ShowSalaryInfo() const
    {
        ShowYourName();
        cout<<"salary: "<<GetPay()<<endl<<endl;
    }
};

class EmployeeHandler
{
private:
    Employee* empList[50];  //Employee 객체의 주소값 저장
    // Employee 클래스를 상속하는 클래스의 객체도 이 배열에 함께 저장 가능
    int empNum;
public:
    EmployeeHandler()
        : empNum(0)
    {    }
    void AddEmployee(Employee* emp)  //Employee 객체의 주소값을 전달 
    // Employee 클래스를 상속하는 클래스 PermenentWorker 객체의 주소값도 전달 가능
    {
        empList[empNum++]=emp;
    }
    void ShowAllSalaryInfo() const{
        
    }
    void ShowTotalSalary() const{
        int sum=0;
        cout<<"salary sum: "<<sum<<endl;
    }
    ~EmployeeHandler()
    {
        for(int i=0; i<empNum; i++)
            delete empList[i];
    }
};

int main(void)
{
    // 직원관리를 목적으로 설계된 컨트롤 클래스의 객체 생성
    EmployeeHandler handler;
    // 직원 등록
    handler.AddEmployee(new PermanentWorker("KIM", 1000));
    handler.AddEmployee(new PermanentWorker("LEE", 1500));
    handler.AddEmployee(new PermanentWorker("JUN", 2000));
    // 이달 지불할 급여 정보
    handler.ShowAllSalaryInfo();
    // 이달 지불할 급여 총합
    handler.ShowTotalSalary();
    return 0;
}

 

EmployeeHandler 객체가 여전히 PermanentWorker 객체를 저장/관리하고 있다.

그래서

영업직 급여 = 월 기본급여 + 인센티브
임시직 급여 = 시간당급여 * 일한시간

형태로 정의를 추가한다.

 

임시직 클래스

// 임시직. 실제 일을 한 시간으로 급여 계산
class TemporaryWorker: public Employee
{
private:
    int workTime; // 이달에 일한 총 시간
    int payPerHour; // 시간 당 급여
public:
    TemporaryWorker(char* name, int pay)
        : Employee(name), workTime(0), payPerHour(pay)
    {  }
    void AddWorkTime(int time) // 일한 시간 추가
    { 
        workTime+=time;
    }
    int GetPay() const // 이달의 급여
    {
        return workTime*payPerHour;
    }
    void ShowSalaryInfo() const
    {
        ShowYourName();
        cout<<"salary: "<<GetPay()<<endl<<endl;
    }
};

 

영업직 클래스

// 영업직 (정규직 일종). Employee가 아닌 PermanentWorker 상속
// 기본급여과 관련된 부분을 멤버로 포함, 상여금 부분만 멤버로 추가
class SalesWorker: public PermanentWorker
{
private:
    int salesResult; //월 판매실적
    double bonusRatio; //상여금 비율
public:
    SalesWorker(char* name, int money, double ratio)
        : PermanentWorker(name, money), salesResult(0), bonusRatio(ratio)
    {   }
    void AddSalesResult(int value)
    {
        salesResult+=value;
    }
    int GetPay() const
    {
        return PermanentWorker::GetPay() //PermanentWorker의 GetPay() 호출
                +(int)(salesResult*bonusRatio);
    }
    void ShowSalaryInfo() const
    {
        ShowYourName();
        cout<<"salary: "<<GetPay()<<endl<<endl; //SalesWorker의 GetPay() 호출
    }
};

 

정규직과 영업직에는 동일 함수 GetPay()와 ShowSalaryInfo()가 있다. (기본클래스 유도클래스에 같은 함수가 있다)

* 함수 오버라이딩 : 함수오버로딩과 혼동 x
기초 클래스와 동일한 이름의 함수를 유도클래스에서 정의되어도 매개변수의 자료형 및 개수가 다르면 함수오버로딩이 되어 전달되는 인자에 따라 호출함수가 결정된다. 함수오버로딩은 상속관계에서도 구성 가능.

오버라이딩 된 기초클래스 함수는 오버라이딩 한 유도클래스 함수에 가려진다.
SalesWorker 클래스 내에서 GetPay를 호출하면 SalesWorker 클래스에 정의된 GetPay함수가 호출된다.

단 SalesWorker 클래스 내에서도 PermanentWorker::GetPay() 이렇게 호출하면 PermanentWorker 클래스 내부의 GetPay가 호출된다.

아래처럼도 호출 가능하다.
seller 객체의 PermanentWorker 클래스에 정의된 ShowSallryInfo()를 호출..이런 뜻

int main(void)
{
	SalesWorker seller("H", 1000, 0.1);
	cout<<seller.PermanentWorker::GetPay()<<endl;
	seller.PermanentWorker::ShowSalaryInfo();
    .... 
}

 

완성본

// EmployeeManager3.cpp

#include <iostream>
#include <cstring>
using namespace std;

class Employee  // 고용인
{
private:
    char name[100];  // 고용인의 이름
public:
    Employee(char* name)
    {
        strcpy(this->name, name);
    }
    void ShowYourName() const
    {
        cout<<"name : "<<name<<endl;
    }
};

class PermanentWorker: public Employee
{
private:
    int salary; //월급여
public:
    PermanentWorker(char* name, int money)
        : Employee(name), salary(money)
    {    }
    int GetPay() const
    {
        return salary;    
    }
    void ShowSalaryInfo() const
    {
        ShowYourName();
        cout<<"salary: "<<GetPay()<<endl<<endl;
    }
};

class EmployeeHandler
{
private:
    Employee* empList[50]; //Employee 객체의 주소값 저장
    // Employee 클래스를 상속하는 클래스의 객체도 이 배열에 함께 저장 가능
    int empNum;
public:
    EmployeeHandler()
        : empNum(0)
    {    }
    void AddEmployee(Employee* emp)  //Employee 객체의 주소값을 전달 
     // Employee 클래스를 상속하는 클래스 PermenentWorker 객체의 주소값도 전달 가능
    {
        empList[empNum++]=emp;
    }
    void ShowAllSalaryInfo() const{
        
    }
    void ShowTotalSalary() const{
        int sum=0;
        cout<<"salary sum: "<<sum<<endl;
    }
    ~EmployeeHandler()
    {
        for(int i=0; i<empNum; i++)
            delete empList[i];
    }
};

// 임시직. 실제 일을 한 시간으로 급여 계산
class TemporaryWorker: public Employee
{
private:
    int workTime; // 이달에 일한 총 시간
    int payPerHour; // 시간 당 급여
public:
    TemporaryWorker(char* name, int pay)
        : Employee(name), workTime(0), payPerHour(pay)
    {  }
    void AddWorkTime(int time) // 일한 시간 추가
    { 
        workTime+=time;
    }
    int GetPay() const // 이달의 급여
    {
        return workTime*payPerHour;
    }
    void ShowSalaryInfo() const
    {
        ShowYourName();
        cout<<"salary: "<<GetPay()<<endl<<endl;
    }
};

// 영업직 (정규직 일종). Employee가 아닌 PermanentWorker 상속
// 기본급여과 관련된 부분을 멤버로 포함, 상여금 부분만 멤버로 추가
class SalesWorker: public PermanentWorker
{
private:
    int salesResult; //월 판매실적
    double bonusRatio; //상여금 비율
public:
    SalesWorker(char* name, int money, double ratio)
        : PermanentWorker(name, money), salesResult(0), bonusRatio(ratio)
    {   }
    void AddSalesResult(int value)
    {
        salesResult+=value;
    }
    int GetPay() const
    {
        return PermanentWorker::GetPay() //PermanentWorker의 GetPay() 호출
                +(int)(salesResult*bonusRatio);
    }
    void ShowSalaryInfo() const
    {
        ShowYourName();
        cout<<"salary: "<<GetPay()<<endl<<endl; //SalesWorker의 GetPay() 호출
        // PermanentWorker 클래스의 ShowSalaryInfo()랑 같은 내용의 함수인데도
        // 오버라이딩 한 이유는 상속이 되었어도 어느 GetPay를 부를 지 컨트롤 할 수 없어서
    }
};

int main(void)
{
    // 직원관리를 목적으로 설계된 컨트롤 클래스의 객체 생성
    EmployeeHandler handler;
    
    // 정규직 등록
    handler.AddEmployee(new PermanentWorker("KIM", 1000));
    handler.AddEmployee(new PermanentWorker("LEE", 1500));
    
    // 임시직 등록
    TemporaryWorker* alba=new TemporaryWorker("Jung", 700);
    alba->AddWorkTime(5); //5시간 일한결과 등록
    handler.AddEmployee(alba);
    
    // 영업직 등록
    SalesWorker* seller=new SalesWorker("Hong", 1000, 0.1)   ;
    seller->AddSalesResult(7000); //영업실적 7000
    handler.AddEmployee(seller);
    
    // 이달 지불할 급여 정보
    handler.ShowAllSalaryInfo();
    
    // 이달 지불할 급여 총합
    handler.ShowTotalSalary();
    return 0;
}

 

메인 함수 내 handler.AddEmployee(new PermanentWorker("KIM", 1000)); 와 같은 코드 4줄에서 발생.
에러 내용 : 인수 1을(를) const char[4]에서 char*  (으)로 변환할 수 없습니다.
이유 : "aa" 문자열은 상수인데 변수에 값을 넣으려 해서.
C에서는 문자열 리터럴이 char의 배열이지만, C++에서는 const char의 배열이다.

에러 수정 : 
1) 포인터가 아닌 배열로 선언
2) (char*) 형식으로 형변환
3) 자료형을 const char* 형태로 바꾸기
4) const_cast<char*> 사용하기

참고 : http://egloos.zum.com/kim0522ms/v/6438724

 

728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,

v