업그레이드~!!

오버로딩과 오버라이딩

SamSiKi 2007. 3. 6. 06:56

우선 오버로딩과 오버라이딩은 용어는 비슷하나 그 차이는 명백합니다.


-오버로딩은 기존에 메소드의 인자를 이용하여서 하나의 함수에 여라가지 기능을 만드는것 입니다.

기존에 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)

반응형