※ 클래스 중에서는 객체생성을 목적으로 정의되지 않는 클래스도 존재한다. !!
따라서 다음과 같은 문장이 만들어진다면 이는 프로그래머의 실수가 틀림없다.
Employee* emp = new Employee("sadf");
하지만, 이는 문법적으로 아무런 문제가 없는 문장이기 때문에, 이러한 실수는 컴파일러에 의해서 발견되지 않는다.
따라서 이러한 경우 가상함수를 "순수 가상함수"로 선언하여 객체의 생성을 문법적으로 막는 것이 좋다.
class Employee
{
private:
char name[100];
public:
virtual int GetPay(void) const = 0; // 순수 가상함수.
virtual void ShowAll(void) const = 0; // 순수 가상함수.
};
"순수 가상함수"란 "함수의 몸체가 정의되지 않은 함수"를 의미한다. 그리고 이를 표현하기 위해 위에서 보이듯 '0의 대입' 을 표시한다. 그런데 이것은 0의 대입을 의미하는 게 아니고, "명시적으로 몸체를 정의하지 않았음" 을 컴파일러에게 알리는 것이다.
따라서 컴파일러는 이 부분에서 함수의 몸체가 정의되지 않았다고 컴파일 오류를 일으키지 않는다. 그러나 Employee 클래스는 순수 가상함수를 지닌, 완전하지 않은 클래스가 되기 때문에 다음과 같이 객체를 생성하려 들면 컴파일 에러가 발생한다.
Employee * emp = new Employee("asdf"); // error 발생!!
이렇게 순수 가상함수를 이용하면 두 가지 이점을 얻을 수 있다.
하나는, 잘못된 객체의 생성을 막을 수 있다.
또 하나는 보다 명확히 명시하는 효과를 얻을 수 있다.
이렇듯 하나 이상의 멤버함수를 순수 가상함수로 선언한 클래스를 가리켜 "추상 클래스(Abstract Class)" 라 한다.
이는 완전하지 않은, 그래서 객체생성이 불가능한 클래스라는 의미를 지닌다.
- 가상함수와 순수 가상함수 그리고 추상 클래스의 대한 예제 -
#include <iostream>
#include <cstring>
using namespace std;
namespace RISK_LEVEL
{
enum
{
RISK_A = 30,
RISK_B = 20,
RISK_C = 10
};
}
class Employee // 추상 클래스
{
private:
char name[100];
public:
Employee(char* name)
{
strcpy(this->name, name);
}
void ShowYourName(void) const
{
cout<<"name: "<<name<<endl;
}
virtual int GetPay(void) const = 0; //순수 가상함수
virtual void ShowSalaryInfo(void) const = 0; //순수 가상함수
};
class PermanentWorker :public Employee
{
private:
int salary;
public:
PermanentWorker(char* name, int money)
:Employee(name), salary(money)
{ }
virtual int GetPay(void) const
{
return salary;
}
virtual void ShowSalaryInfo(void) const
{
ShowYourName();
cout<<"salary: "<<GetPay()<<endl<<endl;
}
};
class TemporaryWorker :public Employee
{
private:
int workTime;
int payPerHour;
public:
TemporaryWorker(char* name, int pay)
:Employee(name), payPerHour(pay), workTime(0)
{ }
void AddworkTime(int time)
{
workTime += time;
}
int GetPay(void) const
{
return workTime * payPerHour;
}
void ShowSalaryInfo(void) const
{
ShowYourName();
cout<<"salary: "<<GetPay()<<endl<<endl;
}
};
class SalesWorker :public PermanentWorker
{
private:
int salesResult;
double bonusRatio;
public:
SalesWorker(char* name, int money, double ratio)
:PermanentWorker(name, money), bonusRatio(ratio), salesResult(0)
{ }
void AddSalesResult(int value)
{
salesResult += value;
}
int GetPay(void) const
{
return PermanentWorker::GetPay() + (int)(salesResult*bonusRatio);
}
void ShowSalaryInfo(void) const
{
ShowYourName();
cout<<"salary: "<<GetPay()<<endl;
}
};
class ForeignSalesWorker :public SalesWorker
{
private:
const int riskPay;
public:
ForeignSalesWorker(char* name, int money, double ratio, int riskPay)
:SalesWorker(name, money, ratio), riskPay(riskPay)
{ }
int GetRisk(void) const
{
return (int)(SalesWorker::GetPay() * (riskPay/100.0));
}
int GetPay(void) const
{
return SalesWorker::GetPay();
}
void ShowSalaryInfo(void) const
{
SalesWorker::ShowSalaryInfo();
cout<<"risk pay: "<<GetRisk()<<endl;
cout<<"sum: "<<GetPay() + GetRisk()<<endl<<endl;
}
};
class EmployeeHandler
{
private:
Employee* empList[50];
int empNum;
public:
EmployeeHandler()
:empNum(0)
{ }
void AddEmployee(Employee* emp)
{
empList[empNum++] = emp;
}
void ShowAllSalaryInfo(void) const
{
for(int i=0; i<empNum; i++)
empList[i]->ShowSalaryInfo();
}
void ShowTotalSalary(void) 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];
}
};
int main(void)
{
EmployeeHandler handler;
ForeignSalesWorker * fseller1 = new ForeignSalesWorker("Hong", 1000, 0.1, RISK_LEVEL::RISK_A);
fseller1->AddSalesResult(7000);
handler.AddEmployee(fseller1);
ForeignSalesWorker * fseller2 = new ForeignSalesWorker("Yoon", 1000, 0.1, RISK_LEVEL::RISK_B);
fseller2->AddSalesResult(7000);
handler.AddEmployee(fseller2);
ForeignSalesWorker * fseller3 = new ForeignSalesWorker("Lee", 1000, 0.1, RISK_LEVEL::RISK_C);
fseller3->AddSalesResult(7000);
handler.AddEmployee(fseller3);
handler.ShowAllSalaryInfo();
return 0;
}