다형성

다형성은 동일한 함수 요소를 호출할 때 서로 다른 클래스의 객체가 다양한 방식으로 반응할 수 있는 기회입니다. 기준 클래스뿐만 아니라 하위 클래스의 동작을 설명하는 범용 메커니즘을 만드는 데 도움이 됩니다.

계속해서 기준 클래스 CShape를 개발하고 쉐이프의 면적을 계산하기 위해 설계된 멤버 함수 GetArea()를 정의하겠습니다. 기준 클래스로부터 물려받아 생산되는 모든 후손 계층에서 우리는 특정 형상의 면적을 계산하는 규칙에 따라 이 기능을 재정의합니다.

사각형(클래스 CSquare)의 경우 면적을 계산하고, 원(클래스 CCircle)의 경우 면적을 반지름 등으로 표현합니다. 기준 클래스의 개체와 모든 하위 클래스의 개체를 모두 저장할 수 있는 CShape 유형의 개체를 저장할 배열을 만들 수 있습니다. 또한 어레이의 각 요소에 대해 동일한 함수를 호출할 수 있습니다.

예제:

//--- 기준 클래스
class CShape
  {
protected
   int            m_type;                // 쉐이프 유형
   int            m_xpos;                // X - 기준점 좌표
   int            m_ypos;                // Y - 기준점 좌표
public:
   void           CShape(){m_type=0;};   // 구성자, type=0
   int            GetType(){return(m_type);};// 쉐이프 유형 반환
virtual
   double         GetArea(){return (0); }// 쉐이프 영역 반환
  };

이제 파생된 모든 클래스에 0 값을 반환하는 getArea() 멤버 함수가 있습니다. 이 기능의 구현은 각 하위 항목마다 다릅니다.

//--- 파생 클래스 써클
class CCircle : public CShape            // 콜론 뒤에 기준 클래스를 정의합니다
  {                                      // 상속할 수 있는
private:
   double         m_radius;              // 원 반지름
 
public:
   void           CCircle(){m_type=1;};  // 구성자, type=1 
   void           SetRadius(double r){m_radius=r;};
   virtual double GetArea(){return (3.14*m_radius*m_radius);}// 써클 영역
  };

클래스 Square의 경우 선언은 동일합니다:

//--- 파생 클래스 사각형
class CSquare : public CShape            // 콜론 뒤에 기준 클래스를 정의합니다
  {                                      // 상속할 수 있는
private:
   double          m_square_side;        // 사각형 면
 
public:
   void            CSquare(){m_type=2;}; // constructor, type=1
   void            SetSide(double s){m_square_side=s;};
   virtual double  GetArea(){return (m_square_side*m_square_side);}// 사각형 면적
  };

사각형과 원의 면적을 계산하려면 m_radius와 m_square_side의 해당 값이 필요하므로 해당 클래스의 선언에 SetRadius()와 SetSide() 함수를 추가했습니다.

하나의 기준 타입 CShape에서 파생된 서로 다른 타입의 객체(CCircle과 CSquare)가 우리 프로그램에서 사용된다고 가정합니다. 다형성을 사용하면 기준 CShape 클래스의 개체 배열을 만들 수 있지만 이 배열을 선언할 때 이러한 개체는 아직 알 수 없으며 유형이 정의되지 않습니다.

배열의 각 요소에 포함될 개체 유형에 대한 결정은 프로그램 실행 중에 직접 이루어집니다. 여기에는 적절한 클래스의 개체의 동적 생성이 포함되므로 개체 대신 객체 포인터를 사용해야 합니다.

new 연산자는 객체의 동적 작성에 사용됩니다. 이러한 각 객체는 delete 연산자를 사용하여 개별적으로 명시적으로 삭제해야 합니다. 따라서 다음 스크립트 예제와 같이 CShape 유형의 포인터 배열을 선언하고 각 요소 (new Class_Name)에 대해 적절한 유형의 개체를 만듭니다:

//+------------------------------------------------------------------+
//| 스크립트 프로그램 시작 함수                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 기준 유형의 개체 포인터 배열을 선언
   CShape *shapes[5];   // CShape 개체에 대한 포인터 배열
 
//--- 여기서 배열을 파생 객체로 채움
//--- CCircle 유형의 개체에 대한 포인터를 선언
   CCircle *circle=new CCircle();
//--- 원형 포인터에서 객체 특성 설정
   circle.SetRadius(2.5);
//--- 포인터 값을 shapes[0]에 배치
   shapes[0]=circle;
 
//--- 다른 CCircle 객체를 만들고 포인터를 shapes[1]에 기록
   circle=new CCircle();
   shapes[1]=circle;
   circle.SetRadius(5);
 
//--- 여기서는 shapes[2]에 대한 값을 설정하기 위해 의도적으로 "잊음"
//circle=new CCircle();
//circle.SetRadius(10);
//shapes[2]=circle;
 
//--- 사용되지 않는 요소에 대해 NULL 설정
   shapes[2]=NULL;
 
//--- CSquare 개체를 만들고 해당 포인터를 shapes[3]에 기록
   CSquare *square=new CSquare();
   square.SetSide(5);
   shapes[3]=square;
 
//--- CSquare 개체를 만들고 해당 포인터를 shapes[4]에 기록
   square=new CSquare();
   square.SetSide(10);
   shapes[4]=square;
 
//--- 포인터가 여러 개 있는데 크기를 파악해야 합니다
   int total=ArraySize(shapes);
//--- 배열의 모든 포인터를 통해 루프를 전달
   for(int i=0; i<5;i++)
     {
      //--- 지정된 인덱스의 포인터가 유효한 경우
      if(CheckPointer(shapes[i])!=POINTER_INVALID)
        {
         //--- 쉐이프의 유형 및 사각형 기록
         PrintFormat("%d 유형의 객체가 %G 정사각형을 갖습니다.",
               shapes[i].GetType(),
               shapes[i].GetArea());
        }
      //--- 포인터가 POINTER_INVALID 유형이 있는 경우
      else
        {
         //--- 오류 통지
         PrintFormat("객체 shapes[%d]이(가) 초기화되지 않았습니다! 포인트는 %s 입니다",
                     i,EnumToString(CheckPointer(shapes[i])));
        }
    }
 
//--- 생성된 모든 동적 개체를 삭제해야 합니다
   for(int i=0;i<total;i++)
     {
      //--- POINTER_DYNAMIC 유형의 포인터가 있는 개체만 삭제할 수 있습니다.
      if(CheckPointer(shapes[i])==POINTER_DYNAMIC)
        {
         //--- 삭제 통지
         PrintFormat("shapes[%d] 삭제",i);
         //--- 포인터로 객체 삭제
         delete shapes[i];
        }
    }
  }

삭제 연산자를 사용하여 객체를 삭제할 때, 포인터 유형을 선택해야 합니다. POINTER_DYNAMIC 포인터가 있는 개체만 삭제를 사용하여 삭제할 수 있습니다. 다른 유형의 포인터의 경우 오류가 반환됩니다.

그러나 다형성은 상속 중 함수의 재정의 외에도 클래스 내에서 서로 다른 매개변수 집합을 가진 동일한 함수의 구현도 포함합니다. 즉, 클래스에는 이름은 같지만 유형 및/또는 매개변수 집합이 다른 여러 함수가 있을 수 있습니다. 이 경우 다형성은 함수 오버로드를 통해 구현됩니다.

더 보기

표준 라이브러리