C++ 상속과 다형성

‍정진철·2022년 9월 28일
0

C++

목록 보기
16/18

참조 : 윤성우의 열혈 c++ 프로그래밍

객체 포인터의 참조관계

  • PartTimeStudent , Student 는 모두 Person 이므로 Person형 포인터 변수는 PartTimeStudent , Student 객체를 모두 가리킬 수 있다 ( 객체의 주소 값을 저장할 수 있다)
  • 따라서 Person * ptr 이 선언됬을때 ptr을 Person 객체로 단언짓지 못하는 이유는 Person을 상속하는 클래스의 객체가 될 수 있기 때문이다.

#include <iostream>
using namespace std;

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

class Student : public Person // Sutdnet 클래스가 Person을 상속함. (Person : 부모 클래스)
{
public:
    void Study() { cout << "Study" << endl; }
};

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

int main(void)
{
    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;
}

급여관리 확장성 문제의 1차적 해결과 함수 오버라이딩

#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 PermanetWorker : public Employee
{
private:
    int salary;

public:
    PermanetWorker(char *name, int money) : Employee(name), salary(money) //부모클래스 초기화
    {
    }
    int GeyPay() const
    {
        return salary;
    }
    void ShowSalaryInfo() const
    {
        ShowYourName();
        cout << "salary: " << GetPay() << endl
             << endl;
    }
};

class EmployeeHandler
{
private:
    Employee *empList[50];
    int empNum;

public:
    EmployeeHandler() : empNum(0) // 디폴트 생성자
    {
    }
    void AddEmployee(Employee *emp)
    {
        empList[empNum++] = emp;
    }
    void ShowAllSalaryInfo() const
    {
        for (int i = 0; i < empNum; i++)
        {
            empList[i]->ShowSalaryInfo();
        }
        void ShowTotalSalary() const
        {
            int sum = 0;
            for (int i = 0; i < empNum; i++)

                sum += empList[i]->GetPay();
            cout << "salary sum: " << sum << endl;
        }
        ~EmployeeHandler()
        {
            for (int i = 0; i < empNum; i++)
            {
                delete empList[i];
            }
        }
    };
1) for (int i = 0; i < empNum; i++)

	 empList[i]->ShowSalaryInfo();

2)  for (int i = 0; i < empNum; i++)

sum += empList[i]->GetPay();
cout << "salary sum: " << sum << endl;

✅  위의 코드에서 ShowSalaryInfo 함수와 GetPay 함수는 실행 불가하다 왜냐하면 현재 EmployeeGHandler 클래스는 Employee *empList[50]; 와 같이 Employee 클래스 객체를 가리키고 있는데 반해 Employee 클래스내 에는 해당 함수가 구현되어 있지 않다.


임시직 : TemporaryWorker

class TemporaryWorker
{
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;
    }
};

영업직: SalesWorker

class SalesWorker : public PermanentWorker
{
private:
    int salesResult;
    double bonusRatio;

public:
    SalesWorker(char *name, int money, double ration) : PermanentWorker(name, money), salesResult(0), bonusRatio(ratio) {}
    void AddSaleResult(int value)
    {
        salesResult += value;
    }
    int GetPay() const
    {
        return PermanentWorker ::GetPay() + (int)(salesResult * bonusRatio);
    }
    void ShowSalaryInfo() const
    {
        ShowYourName();
        cout << "salary: " << GetPay() << endl
             << endl;
    }
}

✅ SalesWokrer 클래스가 PermanentWorker 클래스의 멤버 함수인 GetPay, ShowSalaryInfo 함수 오버라이딩


가상함수 (Virtual Function)

class Base
{
public:
	void BaseFunc() { cout << "Base Function " << endl;}
};

class Derived : public Base
{
public:
	void DerivedFunc() { cout << "Derived Function" << endl;}
}

int main(void)
{
	Base * bptr = new Derived(); // 컴파일 ok !
}

✅ bptr -> DerivedFun() ; // 컴파일 error

이유 : bptr은 Base 클래스의 객체인데 DerivedFunc 의 함수가 Base클래스내 에서 정의되어 있지 않음.

Base * bptr = new Derived(); // 컴파일 성공
Derived * dptr = bptr ; //컴파일 에러

-> Derived 객체가 가리키는 객체는 Base 클래스의 객체일 수 있지만 아닐 수 도 있기에 에러가 발생함. ( 즉 모든 경우에서 100프로 일어날 확률이 발생하지 않으면 컴파일을 에러를 보냄)

✅ 컴파일러는 Type 자체만을 보고 판단을 하기 때문에 bptr은 Base형 포인터로 여기고 bptr이 가리키는 대상이 Base 클래스 객체 일 수도 있을 거라 여긴다.

✅ 반면 ,

Derived * dptr = new Derived(); //컴파일 ok 
Base * bptr = dptr ; //컴파일 Ok

위 코드에서의 Base * bptr = dptr 은 컴파일 성공인데 이유는 Derived 클래스는 Base 클래스를 상속받으므로 직/간접적으로 어떻게든 상속이 되는 객체라고 여기기 때문.


복습

class First
{
public: 
	void FirstFunc() { cout<< "FirstFunc" << endl; }
};

class Second : public Fisrt
{
public:
	void SecondFunc() {cout << "SecondFunc" << endl;}
};

class Third : public Second
{
public:
	void ThirdFunc() { cout << "ThirdFUnc" << endl; }
};

✅ 모두 컴파일 성공하는 케이스

Third * tptr = new Third();
Seoncd * sptr = tptr
First * fprt = sptr;

✅ 각 객체별 접근가능한 함수 종류

tptr -> FirstFunc() ; (O)
tptr -> SecondFunc() ; (O)
tptr -> ThirdFunc() ; (O)

sptr -> FirstFunc(); (O)
sptr -> SecondFunc(); (O)
sptr -> ThirdFunc(); (X)


fptr -> FirstFunc(); (O)
fptr -> SecondFunc(); (X)
fptr -> ThirdFunc(); (X)

함수의 오버라이딩과 포인터 형

Third * tptr = new Third();
Seoncd * sptr = tptr
First * fprt = sptr;

fptr -> MyFunc();
sptr -> MyFunc();
tprt -> MyFunc();
delete tptr; 
return 0;

✅ MyFunc 함수 호출시 해당 클래스 내부에 존재하는 함수가 적용됨을 알 수 있다.


가상함수

class First
{
public: 
	virtual void FirstFunc() { cout<< "FirstFunc" << endl; }
};

class Second : public Fisrt
{
public:
	virtual void SecondFunc() {cout << "SecondFunc" << endl;}
};

class Third : public Second
{
public:
	virtual void ThirdFunc() { cout << "ThirdFUnc" << endl; }
};

✅ 'virtual ' 로 함수 선언시 가장 마지막으로 오버라이딩된 함수 호출


#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;
    }
    virtual int GetPay() const
    {
        return 0;
    }
    virtual void ShowSalaryInfo() const
    {
    }
};

class PermanentWorker : public Employee
{
private:
    int salary;

public:
    PermanetWorker(char *name, int money) : Employee(name), salary(money) //부모클래스 초기화
    {
    }
    int GeyPay() const
    {
        return salary;
    }
    void ShowSalaryInfo() const
    {
        ShowYourName();
        cout << "salary: " << GetPay() << endl
             << endl;
    }
};

class EmployeeHandler
{
private:
    Employee *empList[50];
    int empNum;

public:
    EmployeeHandler() : empNum(0) // 디폴트 생성자
    {
    }
    void AddEmployee(Employee *emp)
    {
        empList[empNum++] = emp;
    }
    void ShowAllSalaryInfo() const
    {
        for (int i = 0; i < empNum; i++)
        {
            empList[i]->ShowSalaryInfo();
        }
        void ShowTotalSalary() const
        {
            int sum = 0;
            for (int i = 0; i < empNum; i++)

                sum += empList[i]->GetPay();
            cout << "salary sum: " << sum << endl;
        }
        ~EmployeeHandler()
        {
            for (int i = 0; i < empNum; i++)
            {
                delete empList[i];
            }
        }
    };

✅ 상속을 통해 연관된 일련의 클래스 PermanentWorker, TemporaryWorker, SalesWorekr에 공통적인 규약(Employee 클래스)을 정의할 수 있음.

이로 인해서 Employee 클래스를 상속하는 모든 클래스의 객체는 Employee 객체로 바라볼 수 있게 된 것.

✅ 참고로 최초의 클래스내(ex: Employee) 에 선언된 virtual 함수를 오버라이딩 할 때 에는 추가적으로 virtual 을 붙여주지 않아도 오버라이딩을 진행한다면 자동적으로 virtual 함수로 인식하게 된다.


순수 가상함수와 추상클래스

class Employee
{
private:
	char name[50];
public:
	Eomployee(char * name) { ...}
    void ShowYourName() const { ... }
    virtual int GetPay() const = 0;
    virtual void ShowSalaryInfo() const = 0 ;
    
}

✅ 몸체가 정의되지 않은 함수를 '순수 가상함수' 라고 하며 하나 이상의 순수 가상함수를 멤버로 두어서 객체 생성이 불가능한 클래스를 가리켜 추상 클래스라고 한다.

✅ 오버라이딩의 관계를 목적으로 정의된 함수들이므로 몸체부분의 정의는 의미가 없다.

✅ 따라서 순수 가상 함수로 대체 가능

virtual int GetPay() const = 0;
virtual void ShowSalaryInfo() const = 0 ;
    

profile
WILL is ALL

0개의 댓글