우선 오버로딩과 오버라이딩은 용어는 비슷하나 그 차이는 명백합니다.
-오버로딩은 기존에 메소드의 인자를 이용하여서 하나의 함수에 여라가지 기능을 만드는것 입니다.
기존에 int a(int a);라는 함수와
또다른 int a(int a, char b)가 존재한다고 가정합니다.
main()에서 호출시 a(10); 이라고 호출하게 되면 첫번째 int a(int a)라는 함수가 호출됩니다. 만약 a(10,'x'); 을 호출했다면 후자인 int a(int a, char b)가 호출되게 되는겁니다.
주로 생성자메소드를 정의할때 많이 사용합니다.
한마디로 같은 함수의 이름을 가지지만 인자 갯수나 타입로 구분해서 사용하는 것이죠.
-객체지향 개념에서 오버로딩(Overloading)만큼이나 중요하면서 상속 개념에서 빼놓을 수 없는 것이 바로 오버라이딩(Overriding)입니다. 오버라이딩이란 상위 클래스에 있는 메서드와 똑같은 메서드를 하위 클래스에서 다시 만드는 행위를 말합니다. 즉 하위 클래스에서 메서드를 재정의하는 것을 말합니다. 메서드 재정의(Overriding)를 개인적으로 '아버지 무시하기'로 부르고 있는데, 그 이유는 아들이 아버지의 메서드를 재정의했을 때 아버지의 메서드를 완전히 무시하는 경향이 있기 때문입니다. 메서드 재정의에 대해서 자세하게 알아보도록 하겠습니다.
-상속 개념에서의 오버라이딩(Overriding)
아버지 클래스를 상속받아 아들 클래스를 만들었을 때, 아버지가 가지고 있던 메서드를 아들 클래스가 다시 만들었다고 가정해 보십시오. 아버지 것은 내 것이고 내 것도 내 것이니, 완벽하게 똑같은 이름의 메서드가 두 개 존재합니다. 아들 클래스의 객체변수를 만들고, 이 객체를 이용해서 메서드를 호출한다면 아들 클래스는 순간 당황하게 될 것입니다. 아버지 것을 사용할까? 내 것을 사용할까? 하지만 아들 클래스의 객체는 유유히 아버지의 것을 무시하고 자신의 것을 사용합니다. 왜냐하면, 자신의 것이 더 소중하니까요! 아버지의 메서드는 완전히 무시당하는 것이죠. 이것을 우리는 오버라이딩(Overriding)이라고 부르고 있습니다.
여기서 virtual 키워드를 왜 사용하는지에 대한 해답이 나오는 겁니다.
virtual키워드를 사용함으로서 나는 부모 메소드를 사용하는것이 아니고 새로 정의한 메소드를 사용하겠다고 선언하는 것 입니다.
궂이 virtual 키워드를 붙이지 않아도 컴파일되는 과정에서 자동으로 분류(물론 순수 가상 함수(=0)일 경우는 제외한 경우입니다.) 해주기는하나 명확히 구분하기 위해서 virtual 키워드를 붙이는 것입니다.
각각의 특징
* 오버로딩 : 1.메서드 이름이 같아야 함
2.리턴형이 같아도 되고 달라고 된다.
3.파라미터의 개수가 달라야함 ,
4.파라미터의 개수 가 같을 경우 자료형이 달라야한다.
5.같은 클래스의 볼록안되 지원된다.
* 오버라이드 : 1. 오버라이드하고자 하는 메소드가 상위 클래스에 존재해야 한다.
2. 메소드의 이름이 같아야 한다.
3. 메소드의 파라미터 개수, 데이터형이 같아야 한다.
4. 메소드의 리턴형이 같아야 한다.
5. 상위 메소드와 동일하거나 더 구체적인 Exception을 발생시켜야 한다.
6. 상위 메소드와 동일하거나 접근 범위가 넓은 접근 제한자를 사용해야 한다
오버로딩 : 새로만든다는 개념
오러라이딩 : 기존에 있는 메소드를 재정의 한다는 개념
-----------------------------------------------------------------------------------------------------
아래는 간단한 오버로딩의 예제소스입니다..
class CPPTestLoading
{
public:
CTestLoading()
{
}
~CTestLoading()
{
}
void Set(int _a,int _b)
{
a = _a;
b = _b;
c = 0;
}
void Set(int _a,int _b,int _c)
{
a = _a;
b = _b;
c = _c;
}
int a;
int b;
int c;
};
void main()
{
CPPTestLoading obj;
obj.Set(1,2);
obj.Set(1,2,3);
}
위의 소스에서처럼 오버로딩을 하면 같은 함수명을 사용하기에 편한것처럼
보이지만 오버로딩을 많이 하면 성능의 저하가 일어날 수 있습니다..
오버로딩을 c언어로 구현을 해보면 금방 알 수가 있죠..
typedef struct CTestLoading
{
int a;
int b;
int c;
}CTestLoading;
void CTestLoading_Set_1(CTestLoading* obj,int _a,int _b)
{
obj->a = _a;
obj->b = _b;
obj->c = 0;
}
void CTestLoading_Set_2(CTestLoading* obj,int _a,int _b,int _c)
{
obj->a = _a;
obj->b = _b;
obj->c = _c;
}
void main()
{
CTestLoading obj;
if(매개변수갯수 == 2 && 자료형이 모두 int형이라면)
CTestLoading_Set_1(&obj,1,2);
else if(매개변수갯수 == 3 && 자료형이 모두 int형이라면)
CTestLoading_Set_2(&obj,1,2,3);
}
위의 소스처럼 오버로딩의 갯수가 많아질수록 if - else if문이 늘어나게
되어 성능이 저하될 수가 있습니다..
c++에서는 이 과정을 프로그래머에게 보여주지 않고 숨기죠..
아래는 간단한 오버라이딩 예제입니다..
class CPPTestRiding
{
public:
CPPTestRiding()
{
}
virtual ~CPPTestRiding()
{
}
virtual void Set(int _a,int _b)
{
a = _a;
b = _b;
}
int a;
int b;
};
class CPPTestRidingChild : public CPPTestRiding
{
public:
CPPTestRidingChild()
{
}
virtual ~CPPTestRidingChild()
{
}
virtual void Set(int _a,int _b)
{
a = _a + 10;
b = _b + 10;
}
};
void main()
{
CPPTestRiding *obj = new CPPTestRidingChild;
obj->Set(10,10);
printf("%d %d",obj->a,obj->b);
delete obj;
}
실행해보시면 main에서의 Set은 Child클래스것을 사용했음을 알 수 있습니다..
그럼 저것을 c언어로 바꾸어서 표현을 해보죠..
(참고로 원래는 malloc과 free를 써야되지만 그냥 new와 delete를 썼음..)
typedef void (*Set)(void* obj,int _a,int _b); // 가상함수의 타입을 정의함..
typedef struct CTestRiding
{
int a;
int b;
Set SetFunc; // c++에서의 가상함수
}CTestRiding;
void CTestRiding_Set(void* obj,int _a,int _b)
{
CTestRiding* castObj = (CTestRiding*)obj;
castObj->a = _a;
castObj->b = _b;
}
CTestRiding* CTestRiding_Create()
{
CTestRiding *obj;
obj = new CTestRiding;
obj->SetFunc = CTestRiding_Set; // 이부분에서 해당객체의 메소드를 재정의함..
return obj;
}
typedef struct CTestRidingChild
{
CTestRiding parent; // 상속을 표현..
}CTestRidingChild;
void CTestRidingChild_Set(void* obj,int _a,int _b)
{
CTestRidingChild* castObj = (CTestRidingChild*)obj;
castObj->parent.a = _a + 10;
castObj->parent.b = _b + 10;
}
CTestRiding* CTestRidingChild_Create()
{
CTestRidingChild *obj;
obj = new CTestRidingChild;
obj->parent.SetFunc = CTestRidingChild_Set; // 이부분에서 해당객체의 메소드를 재정의함..
return ((CTestRiding*)obj);
}
void main()
{
CTestRiding* obj = CTestRidingChild_Create();
obj->SetFunc(obj,10,10);
printf("%d %d\n",obj->a,obj->b);
delete obj;
}
위의 소스를 보시면 알겠지만 함수포인터를 이용하여 가상함수를 구현하고 있습니다.
객체를 Create 할 때 내부적으로 정의된 함수로 세팅을 하게 되는겁니다..
아마 new를 할 때 재정의를 하지 않을까 생각됩니다..
아무튼 포인터를 이용해서 세팅을 해주기때문에 아무리 오버라이딩을 해도
오버로딩처럼 성능의 저하는 안일어나게 되는것이죠..
(하지만 많이 쓰이는 메소드를 오버라이딩하는것은 별로 안좋음..)
(출처 : http://kin.naver.com/db/detail.php?d1id=1&dir_id=10104&eid=DnIPq0ZiUVofjYysZnP+kKJOjwjdYB7t&qb=v8C59rfOtfkgv8C59rbzwMy1+Q)