# C++ 에서는 함수뿐만 아니라 연산자도 오버로딩이 가능하다.

# 연산자 오버로딩은 C++ 을 이해하는데 매우 중요한 요소이다.


# 함수가 오버로딩 되면, 오버로딩 된 수만큼 다양한 기능을 제공하게 된다. 

즉, 이름은 하나이지만 기능은 여러 가지가 되는 셈이다.

마찬가지로 연산자의 오버로딩을 통해서, 기존에 존재하던 연산자의 기본 기능 이외에 다른 기능을 추가할 수 있다.




※ + 뿐만 아니라.. -, /, *, %로도 해보자~!!!


"p1 + p2 가 p1.operator+(p2) 의 다른 표현이다. "

즉, 약속된 변환의 규칙이다. 'operator' 키워드와 '연산자' 를 묶어서 함수의 이름을 정의하면, 함수의 이름을 이용한 함수의 호출뿐만 아니라, 연산자를 이용한 함수의 호출도 허용해준다. 


※ 연산자 오버로딩이란 C++ 이 우리에게 제시하는 하나의 약속에 지나지 않는다.


연산자를 오버로딩 한 함수도 const 로 선언이 가능하다. 


# operator+ 함수는 const로 선언을 하는 것이다 좋다. 덧셈연산이라는 것이 원래 연산의 대상이 되는 피연산자의 값을 변경하는 게 아니고, 새로운 연산의 결과를 만들어내는 것이기 때문이다.



연산자를 오버로딩 하는 두 가지 방법


1. 멤버함수에 의한 연산자 오버로딩.

2. 전역함수에 의한 연산자 오버로딩.


+ 연산자는 전역함수를 이용해서도 오버로딩이 가능하다. 그리고 이렇게 전역함수를 이용해서 오버로딩을 하면 p1 + p2 는 다음과 같이 해석이 된다.

operator+(p1, p2);

즉, 어떻게 오버로딩을 했느냐에 따라 해석하는 방법이 두 가지로 나뉘게 된다. 


※ 참고로, 동일한 자료형을 대상으로 + 연산자를 전역함수 기반으로, 그리고 멤버함수 기반으로 동시에 오버로딩 할 경우, 멤버함수 기반으로 오버로딩 된 함수가 전역함수 기반으로 오버로딩 된 함수보다 우선시되어 호출된다. 

단, 일부 컴파일러는 이러한 상황에서 컴파일 에러를 발생시키기도 하니, 이러한 상황은 가급적 만들지 않는 게 좋다. 



전역함수 기반의 연산자 오버로딩에 대한 일반적인 모델 예 &

friend 선언이 적절히 사용된 예


Point 클래스는 + 연산에 대해서 연산자 오버로딩이 되어 있구나를 생각할 수 있다.

이렇듯 Point 클래스에 삽입된 friend 선언으로 인해서, 이 클래스는 + 연산에 대해 오버로딩이 되어 있다는 정보를 쉽게 확인할 수 있다.


※ 객체지향에는 "전역(Global)"에 대한 개념이 존재하지 않는다. 다만 C++은 C 스타일의 코드구현이 가능한 언어이기 때문에 전역에 대한 개념이 여전히 존재한다. 

따라서 특별한 경우가 아니면, 멤버함수를 기반으로 연산자를 오버로딩 하는 게 낫다.


#include <iostream>

using namespace std;


class Point

{

private:

int xpos, ypos;


public:

Point(int x=0, int y=0)

:xpos(x), ypos(y)

{ }


void Show() const

{

cout<<xpos<<", "<<ypos<<endl;

}


Point& operator+=(const Point &ref)

{

xpos += ref.xpos; 

ypos += ref.ypos;

return *this;

}


Point& operator-=(const Point &ref)

{

xpos -= ref.xpos; 

ypos -= ref.ypos;

return *this;

}


friend Point operator-(const Point &, const Point &);

friend bool operator==(const Point &, const Point &);

friend bool operator!=(const Point &, const Point &);

};


Point operator-(const Point &ref1, const Point &ref2)

{

Point pos(ref1.xpos-ref2.xpos, ref1.ypos-ref2.ypos);

return pos;

}


bool operator==(const Point &ref1, const Point &ref2)

{

if(ref1.xpos == ref2.xpos && ref1.ypos == ref2.ypos)

return true;

else 

return false;

}


bool operator!=(const Point &ref1, const Point &ref2)

{

return !(ref1==ref2); //오버로딩 된 == 연산자를 호출하고 있다.

}


int main(void)

{

Point p1(20, 30);

Point p2(5, 7);

Point p3(5, 7);


(p1-p2).Show(); //객체를 반환하므로 함수호출 가능.

(p2+=p3).Show(); //반환형이 참조형이므로 함수호출 가능.


if(p2==p3) //operator==(p1, p2);랑 100% 동일한 문장.

cout<<"같음"<<endl;

else

cout<<"다름"<<endl;


(p2-p3).Show(); //반환형이 참조형이므로 함수호출 가능.


if(operator!=(p1, p2))         //p1 != p2; 랑 100% 동일한 문장.

cout<<"다름"<<endl;

else

cout<<"같음"<<endl;


return 0;

}





오버로딩이 불가능한 연산자의 종류


# C++ 의 모든 연산자들이 오버로딩의 대상이 되는 것은 아니다.

- 오버로딩이 불가능한 연산자들 -


.                                          멤버 접근 연산자

.*                                        멤버포인터 연산자

::                                        범위 지정 연산자

?:                                        조건 연산자

sizeof                                  바이트 단위 크기 계산

typeid                                  RTTI 관련 연산자

static_cast                           형변환 연산자

dynamic_cast                       형변환 연산자

const_cast                           형변환 연산자

reinterpret_cast                     형변환 연산자


이들 연산자에 대해서 오버로딩을 제한하는 이유는 C++ 의 문법규칙을 보존하기 위함이다.


※ 참고로, 위의 연산자들을 오버로딩 해야만 하는 상황이 딱히 존재하지 않다.



멤버함수 기반으로만 오버로딩이 가능한 연산자


- 멤버함수 기반으로만 오버로딩이 가능한 연산자들 - 


=                                        대입 연산자

()                                       함수 호출 연산자

[]                                      배열 접근 연산자(인덱스 연산자)

->                                      멤버 접근을 위한 포인터 연산자


이들은 객체를 대상으로 진행해야 의미가 통하는 연산자들이기 때문에, 멤버함수 기반으로만 연산자의 오버로딩을 허용한다.




연산자를 오버로딩 하는데 있어서의 주의사항


1. 본래의 의도를 벗어난 형태의 연산자 오버로딩은 좋지 않다.


# 연산자를 오버로딩 할 때에는 연산자의 본래 의도를 가급적 충실히 반영해서, 연산자의 오버로딩으로 인해서 발생하는 혼란스러운 부분을 최소화해야 한다.


2. 연산자의 우선순위와 결합성은 바뀌지 않는다.


# 연산자가 갖는 연산의 기능은 오버로딩 되어도, 연산자가 지니는 우선순위와 결합성은 그대로 따르게 되어 있다.


3. 매개변수의 디폴트 값 설정이 불가능하다.


# 연산자 오버로딩의 특성상 매개변수의 디폴트 값이 설정되면, 함수의 호출관계가 매우 불분명해진다. 따라서 매개변수의 디폴트 값 설정은 허용되지 않는다.


4. 연산자의 순수 기능까지 빼앗을 수는 없다.

int operator+(const int num1, const int num2)    //정의 불가능한 함수

{

 return num1 * num2;

}

# int 형 데이터의 + 연산은 이미 그 의미가 정해져 있다. 이것을 변경하는, 위와 같은 함수의 정의는 허용되지 않는다. 

이처럼 연산자의 기본 기능을 변경하는 형태의 연산자 오버로딩은 허용되지 않는다.




"연산자 오버로딩" 이라고 불리는 이유.


int num = 5+5;

Point p3 = p1 + p2;    //p1과 p2는 Point형 객체


함수가 오버로딩 되면 전달되는 인자에 따라서 호출되는 함수가 달라진다. 이와 유사하게, 위의 두 문장에서 보이듯이, 연산자가 오버로딩 되면, 피연산자의 종류에 따라서 연산의 방식이 달라진다. 그래서 연산자 오버로딩이라 불리는 것이다.

Posted by scii
:

※ 다중 상속을 하였을 때 함수 호출관계의 모호함은 다른 상황에서도 발생할 수 있다.


#include <iostream>

using namespace std;


class Base

{

public:

Base()

cout<<"Base Constructor"<<endl; 

}


void SimpleFunc()

cout<<"BaseOne"<<endl;

}

};


class MiddleDerived_1 :public  Base

{

public:

MiddleDerived_1()

:Base()

{

cout<<"MiddleDerived_1 Constructor"<<endl;

}


void MiddleFunc_1()

{

SimpleFunc();

cout<<"Middlederived_1"<<endl; 

}

};


class MiddleDerived_2 :public  Base

{

public:

MiddleDerived_2()

:Base()

{

cout<<"MiddleDerived_2 Constructor"<<endl;

}


void MiddleFunc_2()

{

SimpleFunc();

cout<<"Middlederived_2"<<endl; 

}

};


class LastDerived :public MiddleDerived_1, public MiddleDerived_2

{

public:

LastDerived()

:MiddleDerived_1(), MiddleDerived_2()

{

cout<<"LastDerived Constructor"<<endl;

}


void ComplexFunc()

{

MiddleFunc_1();

MiddleFunc_2();

MiddleDerived_2::SimpleFunc();    //이렇게 명시해주지않으면 에러발생.

}                                                       //왜냐면 어느클래스에서 쓸 것인지 모르기 때문.

};                                                              //현재 Base Class는 두개이다.


int main(void)

{

cout<<"객체생성 전 ... "<<endl;

LastDerived ldr;


cout<<"객체생성 후 ... "<<endl;

ldr.ComplexFunc();


return 0;

}



위 예제는 LastDerived 클래스가 Base 클래스를 간접적으로 두 번 상속하였다. 다중상속의 모호한 상황이다!!


이렇듯 하나의 객체 내에 두 개의 Base 클래스 멤버가 존재하기 때문에 complexFunc 함수 내에서 이름만 가지고 SimpleFunc 함수를 호출할 수는 없다.

이 경우에는 다음과 같이, 어느 클래스를 통해서 간접 상속한 Base 클래스의 멤버함수를 호출할 것인지 명시해야 한다. 


ex) MiddleDerived_1::SimpleFunc();

// MiddleDerived_1 클래스가 상속한 Base 클래스의 SimpleFunc 함수호출을 명령!


그런데 이러한 상황에서, Base 클래스의 멤버가 LastDerived 객체에 하나씩만 존재하는 것이 타당한 경우가 대부분이다. 

즉, Base 클래스를 딱 한번만 상속하게끔 하는 것이 더 현실적인 해결책이 될 수 있다.

그리고 이를 위한 문법이 바로 "가상 상속" 이다.


ex) class MiddleDerived_1 :virtual public Base

{    };


ex) class MiddleDerived_2 :virtual public Base

{    };






#include <iostream>

using namespace std;


class Base

{

public:

Base()

cout<<"Base Constructor"<<endl; 

}


void SimpleFunc()

cout<<"BaseOne"<<endl;

}

};


class MiddleDerived_1 :virtual public Base

{

public:

MiddleDerived_1()

:Base()

{

cout<<"MiddleDerived_1 Constructor"<<endl;

}


void MiddleFunc_1()

{

SimpleFunc();

cout<<"Middlederived_1"<<endl; 

}

};


class MiddleDerived_2 : virtual public  Base

{

public:

MiddleDerived_2()

:Base()

{

cout<<"MiddleDerived_2 Constructor"<<endl;

}


void MiddleFunc_2()

{

SimpleFunc();

cout<<"Middlederived_2"<<endl; 

}

};


class LastDerived :public MiddleDerived_1, public MiddleDerived_2

{

public:

LastDerived()

:MiddleDerived_1(), MiddleDerived_2()

{

cout<<"LastDerived Constructor"<<endl;

}


void ComplexFunc()

{

MiddleFunc_1();

MiddleFunc_2();

SimpleFunc();        //virtual inheritance(가상 상속) 을 해주었기때문에

}                                     //Base Class는 하나로 오버라이딩되어 명시해주지 않아도 된다.

};                                            //왜냐면, Base Class가 하나밖에 없기 때문.


int main(void)

{

cout<<"객체생성 전 ... "<<endl;

LastDerived ldr;


cout<<"객체생성 후 ... "<<endl;

ldr.ComplexFunc();


return 0;

}



이렇듯, 가상으로 Base 클래스를 상속하는 두 클래스를 다음과 같이 다중으로 상속하게 되면,


class LastDerived : public MiddleDerived_1, public MiddleDerived_2

{    };


LastDerived 객체 내에는 MiddleDerived_1 클래스와 MiddleDerived_2 클래스가 동시에 상속하는 Base 클래스의 멤버가 하나씩만 존재하게 된다.!!


- 위 예제에서 SimpleFunc 함수를 이름만 가지고 호출할 수 있을 것이다. 

그리고 예제의 실행결과를 보면 실제로 Base 클래스의 생성자가 한번만 호출되는 것을 확인할 수 있다.

만약에 가상 상속을 하지 않는다면, Base 클래스의 생성자는 두 번 호출된다.

Posted by scii
:

다중 상속이란, 

둘 이상의 클래스를 동시에 상속하는 것을 말한다. 그리고 C++은 다중상속을 지원하는 객체지향 언어이다. 그런데 다중상속은 제법 논란이 되는 문법이다.


실제로 다중 상속으로만 해결이 가능한 문제는 존재하지 않으니, 굳이 다중 상속을 하기 위해 노력할 필요는 없다. 하지만 누군가는 다중 상속을 예외적으로, 매우 제한적으로 적용할 수도 있는 일이므로, 이에 대한 이해를 위해서라도 다중상속을 공부할 필요는 있다. 



다중 상속의 기본방법



세미콤마를 이용해서 상속의 대상이 되는 클래스를 구분해서 명시할 수 있다. 

그리고 기초클래스를 상속하는 형태는 각각 별도로 지정이 가능하다. 






다중 상속의 모호성(Ambiguous)


  - 다중 상속의 대상이 되는 두 기초 클래스에 동일한 이름의 멤버가 존재하는 경우에는 문제가 발생할 수 있다. 이러한 경우에는 유도 클래스 내에서 멤버의 이름만으로 접근이 불가능하기 때문이다. 

만약에 이름만으로 접근하려 든다면 컴파일러는 다음과 같이 불만을 토로할 것이다.


"도대체 어느 클래스에 선언된 멤버에 접근을 하라는 거야?"



이 문제를 해결하려면 위에처럼 직접적으로 어느 클래스에 정의된 함수인지를 명시해야 한다. 

즉, 다중 상속을 할 때 같은 이름의 함수가 있다면, 그 함수를 호출을 할때에는 

어느 클래스에 정의된 함수의 호출을 원하는지 명시해야 한다. 

Posted by scii
:

가상함수의 동작원리




A 클래스를보면 virtual 가상함수가 존재한다. 이렇듯 한 개 이상의 가상함수를 포함하는 클래스에 대해서는 컴파일러가 "가상함수 테이블" 이란 것을 만든다. 

이를 간단히 "V-Table(virtual table)" 이라고도 하는데, 이는 실제 호출되어야 할 함수의 위치정보를 담고 있는 테이블 이다.


가상테이블을 확인하면 A 클래스의 Func1에 대한 정보가 존재하지 않는다. 


- 이렇듯, 오버라이딩 된 가상함수의 주소정보는 유도 클래스의 가상함수 테이블에 포함되지 않는다. 때문에 오버라이딩 된 가상함수를 호출하면, 무조건 가장 마지막에 오버라이딩을 한 유도 클래스의 멤버함수가 호출되는 것이다.




가상함수 테이블이 참조되는 방식


- main 함수가 호출되기 이전에 가상함수 테이블이 메모리 공간에 할당된다. 참고도 가상함수 테이블은 객체의 생성과 상관없이 메모리 공간에 할당된다. 

이는 가상함수 테이블이 멤버함수의 호출에 사용되는 일종의 데이터이기 때문이다. 


※ 가삼함수를 하나이상 멤버로 지니는 클래스의 객체에는 가상함수 테이블의 주소 값이 저장된다. 물론 이 주소 값은 우리가 직접 참조할 수 있는 주소 값이 아니다. 내부적으로 필요에 의해서 참조되는 주소 값일 뿐이다.



>>>>>> 결론


# B 클래스의 가상함수를 살펴보면, 오버라이딩 된 A 클래스의 Func1 함수에 대한 정보가 없음을 알 수 있다. 

그래서 B클래스의 Func1 함수가 대신 호출되는데, 이것이 바로 가상함수의 호출원리이다.



※ 가상함수 테이블에 의한 속도의 저하

- 위에서 설명했듯이 클래스에 가상함수가 포함되면, 가상함수 테이블이 생성되고, 또 이 테이블을 참조하여 호출될 함수가 결정되기 때문에 실행속도가 감소하기 마련이다. 하지만 그 속도의 차이가 극히 미미하고 또 이러한 단점에도 불구하고 가상함수는 많은 장점을 제공하기 때문에 유용하게 활용되는 것이다.

Posted by scii
:

객체 내에 멤버함수는 실제로 객체 내에 존재하지않는다.

하지만, 진실을 알고 난 이후에도 객체 내에 멤버함수가 존재한다고 인식하고 객체를 바라봐야 한다. 개념적으로는, 그리고 객체지향적 논리를 기준으로는, 객체 내에 멤버함수가 존재한다고 인식하는 게 옳으니 말이다. 



C언어 스타일의 구조체와 전역함수를 이용하여 흉내낸 C++객체의 예))



핵심은 두 개의 구조체 변수(객체)가 함수를 공유하고 있다는 사실이다. 

그리고 실제로 C++의 객체와 멤버함수는 이러한 관계를 갖는다.

즉, 객체가 생성되면 멤버변수는 객체 내에 존재하지만, 멤버함수는 메모리의 한 공간에 별도로 위치하고선, 이 함수가 정의된 클래스의 모든 객체가 이를 공유하는 형태를 취한다. 

그리고 객체가 지니고 있는 멤버변수 대상의 연산이 진행되도록 함수를 호출하는 것이다. 

Posted by scii
:

포인터와의 관련

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


그런데, 이러한 특성은 참조자에도 적용이 된다. 


 : "C++에서, A형 참조자는 A 객체 또는 A 객체를 직접 혹은 간접적으로 상속하는 모든 객체를 참조할 수 있다."


뿐만 아니라 가상함수의 개념도 포인터와 마찬가지로 참조자에도 그대로 적용이 된다. 




33행: obj는 Second를 상속하는 Third 객체이므로, Second형 참조자로 참조가 가능하다. 


34~35행: 컴파일러는 참조자의 자료형을 가지고 함수의 호출 가능성을 판단하기 때문에 First 클래스에 정의된 FirstFunc 함수와 Second 클래스에 정의된 SecondFunc 함수는 호출이 가능하지만, Third 클래스에 정의된 ThirdFunc 함수는 호출이 불가능하다. 



void Function(const First &ref) 

만약, 이러한 문장을 보게되면 다음과 같이 판단할 수 있어야 한다. 


=> First 객체 또는 First를 직접 혹은 간접적으로 상속하는 클래스의 객체가 인자의 대상이 되는구나.

=> 인자로 전달되는 객체의 실제 자료형에 상관없이 함수 내에서는 First 클래스에 정의된 함수만 호출할 수 있겠구나.


※ 상속의 목적이 명확하지 않다면, 상속은 하지 않는 편이 나을 수도 있다.


Posted by scii
:

가상함수 말고도 virtual 키워드를 붙여줘야 할 대상이 하나 더 있다. 그건 바로 "소멸자" 이다. 즉, virtual 선언은 소멸자에도 올 수 있다. 


#include <iostream>

#include <cstring>

using namespace std;


class First

{

private:

char* name;

public:

First(char* name)

{

this->name = new char[strlen(name)+1];

}

~First()

{

cout<<"~First()"<<endl;

delete []name;

}

};


class Second :public First

{

private:

char* name;

public:

Second(char* fName, char* sName)

:First(fName)

{

name = new char[strlen(sName)+1];

}

~Second()

{

cout<<"~Second()"<<endl;

delete []name;

}

};


class Third :public Second

{

private:

char* name;

public:

Third(char* fName, char* sName, char* tName)

:Second(fName, sName)

{

name = new char[strlen(tName)+1];

}

~Third()

{

cout<<"~Third()"<<endl;

delete []name;

}

};


int main()

{

First* f1 = new Third("aaa","bbb","ccc");

delete f1;


return 0;

}



delete f1;     => 이 경우 First 클래스의 소멸자와 Second, Third 클래스의 소멸자가 동시에 호출되어야 한다. 하지만 그렇지 않다. 


실행결과에서 보이듯, 객체의 소멸을 First형 포인터로 명령하니 First 클래스의 소멸자만 호출되었다.

따라서 이러한 경우에는 "메모리의 누수(leak)" 가 발생하게 된다. 

그러니 객체의 소멸과정에서는 delete 연산자에 사용된 포인터 변수의 자료형에 상관없이 모든 소멸자가 호출되어야 한다. 

그리고 이를 위해서는 다음과 같이 소멸자에 virtual 선언을 추가하면 된다.


 #include <iostream>

#include <cstring>

using namespace std;


class First

{

private:

char* name;

public:

First(char* name)

{

this->name = new char[strlen(name)+1];

}

virtual ~First()

{

cout<<"~First()"<<endl;

delete []name;

}

};


class Second :public First

{

private:

char* name;

public:

Second(char* fName, char* sName)

:First(fName)

{

name = new char[strlen(sName)+1];

}

virtual ~Second()

{

cout<<"~Second()"<<endl;

delete []name;

}

};


class Third :public Second

{

private:

char* name;

public:

Third(char* fName, char* sName, char* tName)

:Second(fName, sName)

{

name = new char[strlen(tName)+1];

}

virtual ~Third()

{

cout<<"~Third()"<<endl;

delete []name;

}

};


int main()

{

First* f1 = new Third("aaa","bbb","ccc");

delete f1;


return 0;

}



가상함수와 마찬가지로 소멸자도 상속의 계층구조상 맨 위에 존재하는 기초 클래스의 소멸자만 virtual로 선언하면, 이를 상속하는 유도 클래스의 소멸자들도 모두 "가상 소멸자" 로 선언이 된다. 

그리고 가상 소멸자가 호출되면, 상속의 계층구조상 맨 아래에 존재하는 유도 클래스의 소멸자가 대신 호출되면서, 기초 클래스의 소멸자가 순차적으로 호출된다. 

Posted by scii
:

※ 클래스 중에서는 객체생성을 목적으로 정의되지 않는 클래스도 존재한다. !!

따라서 다음과 같은 문장이 만들어진다면 이는 프로그래머의 실수가 틀림없다.

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;

'Programming > C++' 카테고리의 다른 글

참조자의 참조 가능성  (0) 2012.08.20
가상 소멸자(Virtual Destructor)  (0) 2012.08.20
가상 함수 (Virtual Function)  (0) 2012.08.19
상속과 다형성  (0) 2012.06.23
Control 클래스 & Entity 클래스  (0) 2012.06.22
Posted by scii
: