참조 : 윤성우의 열혈 c++ 프로그래밍
#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;
}
#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 클래스내 에는 해당 함수가 구현되어 있지 않다.
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;
}
};
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 함수 오버라이딩
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 ;